resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmArgumentParser.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 "cmConfigure.h" // IWYU pragma: keep
6
7 #include <cassert>
8 #include <cstddef>
9 #include <functional>
10 #include <map>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include <cm/optional>
16 #include <cm/string_view>
17 #include <cm/type_traits>
18 #include <cmext/string_view>
19
20 #include "cmArgumentParserTypes.h" // IWYU pragma: keep
21
22 template <typename Result>
23 class cmArgumentParser; // IWYU pragma: keep
24
25 class cmMakefile;
26
27 namespace ArgumentParser {
28
29 class ParseResult
30 {
31   std::map<cm::string_view, std::string> KeywordErrors;
32
33 public:
34   explicit operator bool() const { return this->KeywordErrors.empty(); }
35
36   void AddKeywordError(cm::string_view key, cm::string_view text)
37
38   {
39     this->KeywordErrors[key] += text;
40   }
41
42   std::map<cm::string_view, std::string> const& GetKeywordErrors() const
43   {
44     return this->KeywordErrors;
45   }
46
47   bool MaybeReportError(cmMakefile& mf) const;
48 };
49
50 template <typename Result>
51 typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
52                         ParseResult*>::type
53 AsParseResultPtr(Result& result)
54 {
55   return &result;
56 }
57
58 template <typename Result>
59 typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
60                         ParseResult*>::type
61 AsParseResultPtr(Result&)
62 {
63   return nullptr;
64 }
65
66 enum class Continue
67 {
68   No,
69   Yes,
70 };
71
72 struct ExpectAtLeast
73 {
74   std::size_t Count = 0;
75
76   ExpectAtLeast(std::size_t count)
77     : Count(count)
78   {
79   }
80 };
81
82 class Instance;
83 using KeywordAction = std::function<void(Instance&)>;
84 using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
85 using PositionAction =
86   std::function<void(Instance&, std::size_t, cm::string_view)>;
87
88 // using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
89 class KeywordActionMap
90   : public std::vector<std::pair<cm::string_view, KeywordAction>>
91 {
92 public:
93   std::pair<iterator, bool> Emplace(cm::string_view name,
94                                     KeywordAction action);
95   const_iterator Find(cm::string_view name) const;
96 };
97
98 // using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>;
99 class PositionActionMap
100   : public std::vector<std::pair<std::size_t, PositionAction>>
101 {
102 public:
103   std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action);
104   const_iterator Find(std::size_t pos) const;
105 };
106
107 class ActionMap
108 {
109 public:
110   KeywordActionMap Keywords;
111   KeywordNameAction KeywordMissingValue;
112   KeywordNameAction ParsedKeyword;
113   PositionActionMap Positions;
114 };
115
116 class Base
117 {
118 public:
119   using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
120   using Continue = ArgumentParser::Continue;
121   using Instance = ArgumentParser::Instance;
122   using ParseResult = ArgumentParser::ParseResult;
123
124   ArgumentParser::ActionMap Bindings;
125
126   bool MaybeBind(cm::string_view name, KeywordAction action)
127   {
128     return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
129   }
130
131   void Bind(cm::string_view name, KeywordAction action)
132   {
133     bool const inserted = this->MaybeBind(name, std::move(action));
134     assert(inserted);
135     static_cast<void>(inserted);
136   }
137
138   void BindParsedKeyword(KeywordNameAction action)
139   {
140     assert(!this->Bindings.ParsedKeyword);
141     this->Bindings.ParsedKeyword = std::move(action);
142   }
143
144   void BindKeywordMissingValue(KeywordNameAction action)
145   {
146     assert(!this->Bindings.KeywordMissingValue);
147     this->Bindings.KeywordMissingValue = std::move(action);
148   }
149
150   void Bind(std::size_t pos, PositionAction action)
151   {
152     bool const inserted =
153       this->Bindings.Positions.Emplace(pos, std::move(action)).second;
154     assert(inserted);
155     static_cast<void>(inserted);
156   }
157 };
158
159 class Instance
160 {
161 public:
162   Instance(ActionMap const& bindings, ParseResult* parseResult,
163            std::vector<std::string>* unparsedArguments, void* result = nullptr)
164     : Bindings(bindings)
165     , ParseResults(parseResult)
166     , UnparsedArguments(unparsedArguments)
167     , Result(result)
168   {
169   }
170
171   void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
172   void Bind(bool& val);
173   void Bind(std::string& val);
174   void Bind(NonEmpty<std::string>& val);
175   void Bind(Maybe<std::string>& val);
176   void Bind(MaybeEmpty<std::vector<std::string>>& val);
177   void Bind(NonEmpty<std::vector<std::string>>& val);
178   void Bind(std::vector<std::vector<std::string>>& val);
179
180   // cm::optional<> records the presence the keyword to which it binds.
181   template <typename T>
182   void Bind(cm::optional<T>& optVal)
183   {
184     if (!optVal) {
185       optVal.emplace();
186     }
187     this->Bind(*optVal);
188   }
189
190   template <typename Range>
191   void Parse(Range const& args, std::size_t pos = 0)
192   {
193     for (cm::string_view arg : args) {
194       this->Consume(pos++, arg);
195     }
196     this->FinishKeyword();
197   }
198
199 private:
200   ActionMap const& Bindings;
201   ParseResult* ParseResults = nullptr;
202   std::vector<std::string>* UnparsedArguments = nullptr;
203   void* Result = nullptr;
204
205   cm::string_view Keyword;
206   std::size_t KeywordValuesSeen = 0;
207   std::size_t KeywordValuesExpected = 0;
208   std::function<Continue(cm::string_view)> KeywordValueFunc;
209   bool DoneWithPositional = false;
210
211   void Consume(std::size_t pos, cm::string_view arg);
212   void FinishKeyword();
213
214   template <typename Result>
215   friend class ::cmArgumentParser;
216 };
217
218 } // namespace ArgumentParser
219
220 template <typename Result>
221 class cmArgumentParser : private ArgumentParser::Base
222 {
223 public:
224   // I *think* this function could be made `constexpr` when the code is
225   // compiled as C++20.  This would allow building a parser at compile time.
226   template <typename T>
227   cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
228   {
229     this->Base::Bind(name, [member](Instance& instance) {
230       instance.Bind(static_cast<Result*>(instance.Result)->*member);
231     });
232     return *this;
233   }
234
235   cmArgumentParser& Bind(cm::static_string_view name,
236                          Continue (Result::*member)(cm::string_view),
237                          ExpectAtLeast expect = { 1 })
238   {
239     this->Base::Bind(name, [member, expect](Instance& instance) {
240       Result* result = static_cast<Result*>(instance.Result);
241       instance.Bind(
242         [result, member](cm::string_view arg) -> Continue {
243           return (result->*member)(arg);
244         },
245         expect);
246     });
247     return *this;
248   }
249
250   cmArgumentParser& Bind(cm::static_string_view name,
251                          Continue (Result::*member)(cm::string_view,
252                                                     cm::string_view),
253                          ExpectAtLeast expect = { 1 })
254   {
255     this->Base::Bind(name, [member, expect](Instance& instance) {
256       Result* result = static_cast<Result*>(instance.Result);
257       cm::string_view keyword = instance.Keyword;
258       instance.Bind(
259         [result, member, keyword](cm::string_view arg) -> Continue {
260           return (result->*member)(keyword, arg);
261         },
262         expect);
263     });
264     return *this;
265   }
266
267   cmArgumentParser& Bind(cm::static_string_view name,
268                          std::function<Continue(Result&, cm::string_view)> f,
269                          ExpectAtLeast expect = { 1 })
270   {
271     this->Base::Bind(name, [f, expect](Instance& instance) {
272       Result* result = static_cast<Result*>(instance.Result);
273       instance.Bind(
274         [result, &f](cm::string_view arg) -> Continue {
275           return f(*result, arg);
276         },
277         expect);
278     });
279     return *this;
280   }
281
282   cmArgumentParser& Bind(
283     cm::static_string_view name,
284     std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
285     ExpectAtLeast expect = { 1 })
286   {
287     this->Base::Bind(name, [f, expect](Instance& instance) {
288       Result* result = static_cast<Result*>(instance.Result);
289       cm::string_view keyword = instance.Keyword;
290       instance.Bind(
291         [result, keyword, &f](cm::string_view arg) -> Continue {
292           return f(*result, keyword, arg);
293         },
294         expect);
295     });
296     return *this;
297   }
298
299   cmArgumentParser& Bind(std::size_t position,
300                          cm::optional<std::string> Result::*member)
301   {
302     this->Base::Bind(
303       position,
304       [member](Instance& instance, std::size_t, cm::string_view arg) {
305         Result* result = static_cast<Result*>(instance.Result);
306         result->*member = arg;
307       });
308     return *this;
309   }
310
311   cmArgumentParser& BindParsedKeywords(
312     std::vector<cm::string_view> Result::*member)
313   {
314     this->Base::BindParsedKeyword(
315       [member](Instance& instance, cm::string_view arg) {
316         (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
317       });
318     return *this;
319   }
320
321   template <typename Range>
322   bool Parse(Result& result, Range const& args,
323              std::vector<std::string>* unparsedArguments,
324              std::size_t pos = 0) const
325   {
326     using ArgumentParser::AsParseResultPtr;
327     ParseResult* parseResultPtr = AsParseResultPtr(result);
328     Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
329                       &result);
330     instance.Parse(args, pos);
331     return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
332   }
333
334   template <typename Range>
335   Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
336                std::size_t pos = 0) const
337   {
338     Result result;
339     this->Parse(result, args, unparsedArguments, pos);
340     return result;
341   }
342 };
343
344 template <>
345 class cmArgumentParser<void> : private ArgumentParser::Base
346 {
347 public:
348   template <typename T>
349   cmArgumentParser& Bind(cm::static_string_view name, T& ref)
350   {
351     this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
352     return *this;
353   }
354
355   cmArgumentParser& Bind(cm::static_string_view name,
356                          std::function<Continue(cm::string_view)> f,
357                          ExpectAtLeast expect = { 1 })
358   {
359     this->Base::Bind(name, [f, expect](Instance& instance) {
360       instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
361                     expect);
362     });
363     return *this;
364   }
365
366   cmArgumentParser& Bind(
367     cm::static_string_view name,
368     std::function<Continue(cm::string_view, cm::string_view)> f,
369     ExpectAtLeast expect = { 1 })
370   {
371     this->Base::Bind(name, [f, expect](Instance& instance) {
372       cm::string_view keyword = instance.Keyword;
373       instance.Bind(
374         [keyword, &f](cm::string_view arg) -> Continue {
375           return f(keyword, arg);
376         },
377         expect);
378     });
379     return *this;
380   }
381
382   cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
383   {
384     this->Base::Bind(position,
385                      [&ref](Instance&, std::size_t, cm::string_view arg) {
386                        ref = std::string(arg);
387                      });
388     return *this;
389   }
390
391   cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
392   {
393     this->Base::BindParsedKeyword(
394       [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
395     return *this;
396   }
397
398   template <typename Range>
399   ParseResult Parse(Range const& args,
400                     std::vector<std::string>* unparsedArguments,
401                     std::size_t pos = 0) const
402   {
403     ParseResult parseResult;
404     Instance instance(this->Bindings, &parseResult, unparsedArguments);
405     instance.Parse(args, pos);
406     return parseResult;
407   }
408
409 protected:
410   using Base::Instance;
411   using Base::BindKeywordMissingValue;
412
413   template <typename T>
414   bool Bind(cm::string_view name, T& ref)
415   {
416     return this->MaybeBind(name,
417                            [&ref](Instance& instance) { instance.Bind(ref); });
418   }
419 };