/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once #include #include "cmStringAlgorithms.h" #include "cmSystemTools.h" template 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 StoreCall; template 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(func)) { } template 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(func)) { } template 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(func)) { } template 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(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 bool parse(std::string const& input, T& index, std::vector 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(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(state)...) ? ParseMode::Valid : ParseMode::Invalid; } else { parseState = ParseMode::ValueError; } } else { parseState = this->StoreCall(allArgs[nextValueIndex], std::forward(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(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(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(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(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 static std::function setToTrue(Values&&... values) { return ArgumentLambdaHelper::generateSetToTrue( std::forward(values)...); } template static std::function setToValue(Values&&... values) { return ArgumentLambdaHelper::generateSetToValue( std::forward(values)...); } private: template class ArgumentLambdaHelper; template class ArgumentLambdaHelper { public: static std::function generateSetToTrue(bool& value1) { return [&value1](const std::string&, CallState&&...) -> bool { value1 = true; return true; }; } static std::function generateSetToTrue(bool& value1, bool& value2) { return [&value1, &value2](const std::string&, CallState&&...) -> bool { value1 = true; value2 = true; return true; }; } static std::function generateSetToValue(std::string& value1) { return [&value1](const std::string& arg, CallState&&...) -> bool { value1 = arg; return true; }; } static std::function generateSetToValue(cm::optional& 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); } };