| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #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); | |
| } | |
| private: | |
| 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); | |
| } | |
| }; |