| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #pragma once |
| |
| #include <cm/optional> |
| |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| |
| template <typename FunctionSignature> |
| struct cmCommandLineArgument |
| { |
| enum class Values |
| { |
| Zero, |
| One, |
| Two, |
| ZeroOrOne, |
| OneOrMore |
| }; |
| |
| enum class RequiresSeparator |
| { |
| Yes, |
| No |
| }; |
| |
| enum class ParseMode |
| { |
| Valid, |
| Invalid, |
| SyntaxError, |
| ValueError |
| }; |
| |
| std::string InvalidSyntaxMessage; |
| std::string InvalidValueMessage; |
| std::string Name; |
| Values Type; |
| RequiresSeparator SeparatorNeeded; |
| std::function<FunctionSignature> StoreCall; |
| |
| template <typename FunctionType> |
| cmCommandLineArgument(std::string n, Values t, FunctionType&& func) |
| : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n)) |
| , InvalidValueMessage(cmStrCat("Invalid value used with ", n)) |
| , Name(std::move(n)) |
| , Type(t) |
| , SeparatorNeeded(RequiresSeparator::Yes) |
| , StoreCall(std::forward<FunctionType>(func)) |
| { |
| } |
| |
| template <typename FunctionType> |
| cmCommandLineArgument(std::string n, Values t, RequiresSeparator s, |
| FunctionType&& func) |
| : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n)) |
| , InvalidValueMessage(cmStrCat("Invalid value used with ", n)) |
| , Name(std::move(n)) |
| , Type(t) |
| , SeparatorNeeded(s) |
| , StoreCall(std::forward<FunctionType>(func)) |
| { |
| } |
| |
| template <typename FunctionType> |
| cmCommandLineArgument(std::string n, std::string failedMsg, Values t, |
| FunctionType&& func) |
| : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n)) |
| , InvalidValueMessage(std::move(failedMsg)) |
| , Name(std::move(n)) |
| , Type(t) |
| , SeparatorNeeded(RequiresSeparator::Yes) |
| , StoreCall(std::forward<FunctionType>(func)) |
| { |
| } |
| |
| template <typename FunctionType> |
| cmCommandLineArgument(std::string n, std::string failedMsg, Values t, |
| RequiresSeparator s, FunctionType&& func) |
| : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n)) |
| , InvalidValueMessage(std::move(failedMsg)) |
| , Name(std::move(n)) |
| , Type(t) |
| , SeparatorNeeded(s) |
| , StoreCall(std::forward<FunctionType>(func)) |
| { |
| } |
| |
| bool matches(std::string const& input) const |
| { |
| bool matched = false; |
| if (this->Type == Values::Zero) { |
| matched = (input == this->Name); |
| } else if (this->SeparatorNeeded == RequiresSeparator::No) { |
| matched = cmHasPrefix(input, this->Name); |
| } else if (cmHasPrefix(input, this->Name)) { |
| if (input.size() == this->Name.size()) { |
| matched = true; |
| } else { |
| matched = |
| (input[this->Name.size()] == '=' || input[this->Name.size()] == ' '); |
| } |
| } |
| return matched; |
| } |
| |
| template <typename T, typename... CallState> |
| bool parse(std::string const& input, T& index, |
| std::vector<std::string> const& allArgs, |
| CallState&&... state) const |
| { |
| ParseMode parseState = ParseMode::Valid; |
| |
| if (this->Type == Values::Zero) { |
| if (input.size() == this->Name.size()) { |
| parseState = |
| this->StoreCall(std::string{}, std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| } else { |
| parseState = ParseMode::SyntaxError; |
| } |
| |
| } else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) { |
| if (input.size() == this->Name.size()) { |
| auto nextValueIndex = index + 1; |
| if (nextValueIndex >= allArgs.size() || |
| allArgs[nextValueIndex][0] == '-') { |
| if (this->Type == Values::ZeroOrOne) { |
| parseState = |
| this->StoreCall(std::string{}, std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| } else { |
| parseState = ParseMode::ValueError; |
| } |
| } else { |
| parseState = this->StoreCall(allArgs[nextValueIndex], |
| std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| index = nextValueIndex; |
| } |
| } else { |
| auto value = this->extract_single_value(input, parseState); |
| if (parseState == ParseMode::Valid) { |
| parseState = |
| this->StoreCall(value, std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| } |
| } |
| } else if (this->Type == Values::Two) { |
| if (input.size() == this->Name.size()) { |
| if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' || |
| allArgs[index + 2][0] == '-') { |
| parseState = ParseMode::ValueError; |
| } else { |
| index += 2; |
| parseState = |
| this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]), |
| std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| } |
| } |
| } else if (this->Type == Values::OneOrMore) { |
| if (input.size() == this->Name.size()) { |
| auto nextValueIndex = index + 1; |
| if (nextValueIndex >= allArgs.size() || |
| allArgs[nextValueIndex][0] == '-') { |
| parseState = ParseMode::ValueError; |
| } else { |
| std::string buffer = allArgs[nextValueIndex++]; |
| while (nextValueIndex < allArgs.size() && |
| allArgs[nextValueIndex][0] != '-') { |
| buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]); |
| } |
| parseState = |
| this->StoreCall(buffer, std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| index = (nextValueIndex - 1); |
| } |
| } else { |
| auto value = this->extract_single_value(input, parseState); |
| if (parseState == ParseMode::Valid) { |
| parseState = |
| this->StoreCall(value, std::forward<CallState>(state)...) |
| ? ParseMode::Valid |
| : ParseMode::Invalid; |
| } |
| } |
| } |
| |
| if (parseState == ParseMode::SyntaxError) { |
| cmSystemTools::Error( |
| cmStrCat("'", input, "'", this->InvalidSyntaxMessage)); |
| } else if (parseState == ParseMode::ValueError) { |
| cmSystemTools::Error(this->InvalidValueMessage); |
| } |
| return (parseState == ParseMode::Valid); |
| } |
| |
| template <typename... Values> |
| static std::function<FunctionSignature> setToTrue(Values&&... values) |
| { |
| return ArgumentLambdaHelper<FunctionSignature>::generateSetToTrue( |
| std::forward<Values>(values)...); |
| } |
| |
| template <typename... Values> |
| static std::function<FunctionSignature> setToValue(Values&&... values) |
| { |
| return ArgumentLambdaHelper<FunctionSignature>::generateSetToValue( |
| std::forward<Values>(values)...); |
| } |
| |
| private: |
| template <typename T> |
| class ArgumentLambdaHelper; |
| |
| template <typename... CallState> |
| class ArgumentLambdaHelper<bool(const std::string&, CallState...)> |
| { |
| public: |
| static std::function<bool(const std::string&, CallState...)> |
| generateSetToTrue(bool& value1) |
| { |
| return [&value1](const std::string&, CallState&&...) -> bool { |
| value1 = true; |
| return true; |
| }; |
| } |
| |
| static std::function<bool(const std::string&, CallState...)> |
| generateSetToTrue(bool& value1, bool& value2) |
| { |
| return [&value1, &value2](const std::string&, CallState&&...) -> bool { |
| value1 = true; |
| value2 = true; |
| return true; |
| }; |
| } |
| |
| static std::function<bool(const std::string&, CallState...)> |
| generateSetToValue(std::string& value1) |
| { |
| return [&value1](const std::string& arg, CallState&&...) -> bool { |
| value1 = arg; |
| return true; |
| }; |
| } |
| |
| static std::function<bool(const std::string&, CallState...)> |
| generateSetToValue(cm::optional<std::string>& value1) |
| { |
| return [&value1](const std::string& arg, CallState&&...) -> bool { |
| value1 = arg; |
| return true; |
| }; |
| } |
| }; |
| |
| std::string extract_single_value(std::string const& input, |
| ParseMode& parseState) const |
| { |
| // parse the string to get the value |
| auto possible_value = cm::string_view(input).substr(this->Name.size()); |
| if (possible_value.empty()) { |
| parseState = ParseMode::ValueError; |
| } else if (possible_value[0] == '=') { |
| possible_value.remove_prefix(1); |
| if (possible_value.empty()) { |
| parseState = ParseMode::ValueError; |
| } |
| } |
| if (parseState == ParseMode::Valid && possible_value[0] == ' ') { |
| possible_value.remove_prefix(1); |
| } |
| return std::string(possible_value); |
| } |
| }; |