1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
5 #include "cmStringAlgorithms.h"
6 #include "cmSystemTools.h"
8 template <typename FunctionSignature>
9 struct cmCommandLineArgument
20 enum class RequiresSeparator
34 std::string InvalidSyntaxMessage;
35 std::string InvalidValueMessage;
38 RequiresSeparator SeparatorNeeded;
39 std::function<FunctionSignature> StoreCall;
41 template <typename FunctionType>
42 cmCommandLineArgument(std::string n, Values t, FunctionType&& func)
43 : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
44 , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
47 , SeparatorNeeded(RequiresSeparator::Yes)
48 , StoreCall(std::forward<FunctionType>(func))
52 template <typename FunctionType>
53 cmCommandLineArgument(std::string n, Values t, RequiresSeparator s,
55 : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
56 , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
60 , StoreCall(std::forward<FunctionType>(func))
64 template <typename FunctionType>
65 cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
67 : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
68 , InvalidValueMessage(std::move(failedMsg))
71 , SeparatorNeeded(RequiresSeparator::Yes)
72 , StoreCall(std::forward<FunctionType>(func))
76 template <typename FunctionType>
77 cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
78 RequiresSeparator s, FunctionType&& func)
79 : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
80 , InvalidValueMessage(std::move(failedMsg))
84 , StoreCall(std::forward<FunctionType>(func))
88 bool matches(std::string const& input) const
91 if (this->Type == Values::Zero) {
92 matched = (input == this->Name);
93 } else if (this->SeparatorNeeded == RequiresSeparator::No) {
94 matched = cmHasPrefix(input, this->Name);
95 } else if (cmHasPrefix(input, this->Name)) {
96 if (input.size() == this->Name.size()) {
100 (input[this->Name.size()] == '=' || input[this->Name.size()] == ' ');
106 template <typename T, typename... CallState>
107 bool parse(std::string const& input, T& index,
108 std::vector<std::string> const& allArgs,
109 CallState&&... state) const
111 ParseMode parseState = ParseMode::Valid;
113 if (this->Type == Values::Zero) {
114 if (input.size() == this->Name.size()) {
116 this->StoreCall(std::string{}, std::forward<CallState>(state)...)
118 : ParseMode::Invalid;
120 parseState = ParseMode::SyntaxError;
123 } else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) {
124 if (input.size() == this->Name.size()) {
125 auto nextValueIndex = index + 1;
126 if (nextValueIndex >= allArgs.size() ||
127 allArgs[nextValueIndex][0] == '-') {
128 if (this->Type == Values::ZeroOrOne) {
130 this->StoreCall(std::string{}, std::forward<CallState>(state)...)
132 : ParseMode::Invalid;
134 parseState = ParseMode::ValueError;
137 parseState = this->StoreCall(allArgs[nextValueIndex],
138 std::forward<CallState>(state)...)
140 : ParseMode::Invalid;
141 index = nextValueIndex;
144 auto value = this->extract_single_value(input, parseState);
145 if (parseState == ParseMode::Valid) {
147 this->StoreCall(value, std::forward<CallState>(state)...)
149 : ParseMode::Invalid;
152 } else if (this->Type == Values::Two) {
153 if (input.size() == this->Name.size()) {
154 if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
155 allArgs[index + 2][0] == '-') {
156 parseState = ParseMode::ValueError;
160 this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
161 std::forward<CallState>(state)...)
163 : ParseMode::Invalid;
166 } else if (this->Type == Values::OneOrMore) {
167 if (input.size() == this->Name.size()) {
168 auto nextValueIndex = index + 1;
169 if (nextValueIndex >= allArgs.size() ||
170 allArgs[nextValueIndex][0] == '-') {
171 parseState = ParseMode::ValueError;
173 std::string buffer = allArgs[nextValueIndex++];
174 while (nextValueIndex < allArgs.size() &&
175 allArgs[nextValueIndex][0] != '-') {
176 buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]);
179 this->StoreCall(buffer, std::forward<CallState>(state)...)
181 : ParseMode::Invalid;
182 index = (nextValueIndex - 1);
185 auto value = this->extract_single_value(input, parseState);
186 if (parseState == ParseMode::Valid) {
188 this->StoreCall(value, std::forward<CallState>(state)...)
190 : ParseMode::Invalid;
195 if (parseState == ParseMode::SyntaxError) {
196 cmSystemTools::Error(
197 cmStrCat("'", input, "'", this->InvalidSyntaxMessage));
198 } else if (parseState == ParseMode::ValueError) {
199 cmSystemTools::Error(this->InvalidValueMessage);
201 return (parseState == ParseMode::Valid);
204 template <typename... Values>
205 static std::function<FunctionSignature> setToTrue(Values&&... values)
207 return ArgumentLambdaHelper<FunctionSignature>::generateSetToTrue(
208 std::forward<Values>(values)...);
211 template <typename... Values>
212 static std::function<FunctionSignature> setToValue(Values&&... values)
214 return ArgumentLambdaHelper<FunctionSignature>::generateSetToValue(
215 std::forward<Values>(values)...);
219 template <typename T>
220 class ArgumentLambdaHelper;
222 template <typename... CallState>
223 class ArgumentLambdaHelper<bool(const std::string&, CallState...)>
226 static std::function<bool(const std::string&, CallState...)>
227 generateSetToTrue(bool& value1)
229 return [&value1](const std::string&, CallState&&...) -> bool {
235 static std::function<bool(const std::string&, CallState...)>
236 generateSetToTrue(bool& value1, bool& value2)
238 return [&value1, &value2](const std::string&, CallState&&...) -> bool {
245 static std::function<bool(const std::string&, CallState...)>
246 generateSetToValue(std::string& value1)
248 return [&value1](const std::string& arg, CallState&&...) -> bool {
255 std::string extract_single_value(std::string const& input,
256 ParseMode& parseState) const
258 // parse the string to get the value
259 auto possible_value = cm::string_view(input).substr(this->Name.size());
260 if (possible_value.empty()) {
261 parseState = ParseMode::ValueError;
262 } else if (possible_value[0] == '=') {
263 possible_value.remove_prefix(1);
264 if (possible_value.empty()) {
265 parseState = ParseMode::ValueError;
268 if (parseState == ParseMode::Valid && possible_value[0] == ' ') {
269 possible_value.remove_prefix(1);
271 return std::string(possible_value);