resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCommandLineArgument.h
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #pragma once
4
5 #include "cmStringAlgorithms.h"
6 #include "cmSystemTools.h"
7
8 template <typename FunctionSignature>
9 struct cmCommandLineArgument
10 {
11   enum class Values
12   {
13     Zero,
14     One,
15     Two,
16     ZeroOrOne,
17     OneOrMore
18   };
19
20   enum class RequiresSeparator
21   {
22     Yes,
23     No
24   };
25
26   enum class ParseMode
27   {
28     Valid,
29     Invalid,
30     SyntaxError,
31     ValueError
32   };
33
34   std::string InvalidSyntaxMessage;
35   std::string InvalidValueMessage;
36   std::string Name;
37   Values Type;
38   RequiresSeparator SeparatorNeeded;
39   std::function<FunctionSignature> StoreCall;
40
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))
45     , Name(std::move(n))
46     , Type(t)
47     , SeparatorNeeded(RequiresSeparator::Yes)
48     , StoreCall(std::forward<FunctionType>(func))
49   {
50   }
51
52   template <typename FunctionType>
53   cmCommandLineArgument(std::string n, Values t, RequiresSeparator s,
54                         FunctionType&& func)
55     : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
56     , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
57     , Name(std::move(n))
58     , Type(t)
59     , SeparatorNeeded(s)
60     , StoreCall(std::forward<FunctionType>(func))
61   {
62   }
63
64   template <typename FunctionType>
65   cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
66                         FunctionType&& func)
67     : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
68     , InvalidValueMessage(std::move(failedMsg))
69     , Name(std::move(n))
70     , Type(t)
71     , SeparatorNeeded(RequiresSeparator::Yes)
72     , StoreCall(std::forward<FunctionType>(func))
73   {
74   }
75
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))
81     , Name(std::move(n))
82     , Type(t)
83     , SeparatorNeeded(s)
84     , StoreCall(std::forward<FunctionType>(func))
85   {
86   }
87
88   bool matches(std::string const& input) const
89   {
90     bool matched = false;
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()) {
97         matched = true;
98       } else {
99         matched =
100           (input[this->Name.size()] == '=' || input[this->Name.size()] == ' ');
101       }
102     }
103     return matched;
104   }
105
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
110   {
111     ParseMode parseState = ParseMode::Valid;
112
113     if (this->Type == Values::Zero) {
114       if (input.size() == this->Name.size()) {
115         parseState =
116           this->StoreCall(std::string{}, std::forward<CallState>(state)...)
117           ? ParseMode::Valid
118           : ParseMode::Invalid;
119       } else {
120         parseState = ParseMode::SyntaxError;
121       }
122
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) {
129             parseState =
130               this->StoreCall(std::string{}, std::forward<CallState>(state)...)
131               ? ParseMode::Valid
132               : ParseMode::Invalid;
133           } else {
134             parseState = ParseMode::ValueError;
135           }
136         } else {
137           parseState = this->StoreCall(allArgs[nextValueIndex],
138                                        std::forward<CallState>(state)...)
139             ? ParseMode::Valid
140             : ParseMode::Invalid;
141           index = nextValueIndex;
142         }
143       } else {
144         auto value = this->extract_single_value(input, parseState);
145         if (parseState == ParseMode::Valid) {
146           parseState =
147             this->StoreCall(value, std::forward<CallState>(state)...)
148             ? ParseMode::Valid
149             : ParseMode::Invalid;
150         }
151       }
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;
157         } else {
158           index += 2;
159           parseState =
160             this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
161                             std::forward<CallState>(state)...)
162             ? ParseMode::Valid
163             : ParseMode::Invalid;
164         }
165       }
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;
172         } else {
173           std::string buffer = allArgs[nextValueIndex++];
174           while (nextValueIndex < allArgs.size() &&
175                  allArgs[nextValueIndex][0] != '-') {
176             buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]);
177           }
178           parseState =
179             this->StoreCall(buffer, std::forward<CallState>(state)...)
180             ? ParseMode::Valid
181             : ParseMode::Invalid;
182           index = (nextValueIndex - 1);
183         }
184       } else {
185         auto value = this->extract_single_value(input, parseState);
186         if (parseState == ParseMode::Valid) {
187           parseState =
188             this->StoreCall(value, std::forward<CallState>(state)...)
189             ? ParseMode::Valid
190             : ParseMode::Invalid;
191         }
192       }
193     }
194
195     if (parseState == ParseMode::SyntaxError) {
196       cmSystemTools::Error(
197         cmStrCat("'", input, "'", this->InvalidSyntaxMessage));
198     } else if (parseState == ParseMode::ValueError) {
199       cmSystemTools::Error(this->InvalidValueMessage);
200     }
201     return (parseState == ParseMode::Valid);
202   }
203
204   template <typename... Values>
205   static std::function<FunctionSignature> setToTrue(Values&&... values)
206   {
207     return ArgumentLambdaHelper<FunctionSignature>::generateSetToTrue(
208       std::forward<Values>(values)...);
209   }
210
211   template <typename... Values>
212   static std::function<FunctionSignature> setToValue(Values&&... values)
213   {
214     return ArgumentLambdaHelper<FunctionSignature>::generateSetToValue(
215       std::forward<Values>(values)...);
216   }
217
218 private:
219   template <typename T>
220   class ArgumentLambdaHelper;
221
222   template <typename... CallState>
223   class ArgumentLambdaHelper<bool(const std::string&, CallState...)>
224   {
225   public:
226     static std::function<bool(const std::string&, CallState...)>
227     generateSetToTrue(bool& value1)
228     {
229       return [&value1](const std::string&, CallState&&...) -> bool {
230         value1 = true;
231         return true;
232       };
233     }
234
235     static std::function<bool(const std::string&, CallState...)>
236     generateSetToTrue(bool& value1, bool& value2)
237     {
238       return [&value1, &value2](const std::string&, CallState&&...) -> bool {
239         value1 = true;
240         value2 = true;
241         return true;
242       };
243     }
244
245     static std::function<bool(const std::string&, CallState...)>
246     generateSetToValue(std::string& value1)
247     {
248       return [&value1](const std::string& arg, CallState&&...) -> bool {
249         value1 = arg;
250         return true;
251       };
252     }
253   };
254
255   std::string extract_single_value(std::string const& input,
256                                    ParseMode& parseState) const
257   {
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;
266       }
267     }
268     if (parseState == ParseMode::Valid && possible_value[0] == ' ') {
269       possible_value.remove_prefix(1);
270     }
271     return std::string(possible_value);
272   }
273 };