• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <cstdint>
20 #include <functional>
21 #include <optional>
22 #include <ostream>
23 #include <string>
24 #include <vector>
25 
26 /* Support for parsing individual flags out of a larger list of flags. This
27  * supports externally determining the order that flags are evaluated in, and
28  * incrementally integrating with existing flag parsing implementations.
29  *
30  * Start with Flag() or one of the GflagsCompatFlag(...) functions to create new
31  * flags. These flags should be aggregated through the application through some
32  * other mechanism and then evaluated individually with Flag::Parse or together
33  * with ParseFlags on arguments. */
34 
35 namespace cuttlefish {
36 
37 /* The matching behavior used with the name in `FlagAlias::name`. */
38 enum class FlagAliasMode {
39   /* Match arguments of the form `<name><value>`. In practice, <name> usually
40    * looks like "-flag=" or "--flag=", where the "-" and "=" are included in
41    * parsing. */
42   kFlagPrefix,
43   /* Match arguments of the form `<name>`. In practice, <name> will look like
44    * "-flag" or "--flag". */
45   kFlagExact,
46   /* Match a pair of arguments of the form `<name>` `<value>`. In practice,
47    * <name> will look like "-flag" or "--flag". */
48   kFlagConsumesFollowing,
49   /* Match a sequence of arguments of the form `<name>` `<value>` `<value>`.
50    * This uses heuristics to try to determine when `<value>` is actually another
51    * flag. */
52   kFlagConsumesArbitrary,
53 };
54 
55 /* A single matching rule for a `Flag`. One `Flag` can have multiple rules. */
56 struct FlagAlias {
57   FlagAliasMode mode;
58   std::string name;
59 };
60 
61 std::ostream& operator<<(std::ostream&, const FlagAlias&);
62 
63 /* A successful match in an argument list from a `FlagAlias` inside a `Flag`.
64  * The `key` value corresponds to `FlagAlias::name`. For a match of
65  * `FlagAliasMode::kFlagExact`, `key` and `value` will both be the `name`. */
66 struct FlagMatch {
67   std::string key;
68   std::string value;
69 };
70 
71 class Flag {
72  public:
73   /* Add an alias that triggers matches and calls to the `Setter` function. */
74   Flag& Alias(const FlagAlias& alias) &;
75   Flag Alias(const FlagAlias& alias) &&;
76   /* Set help text, visible in the class ostream writer method. Optional. */
77   Flag& Help(const std::string&) &;
78   Flag Help(const std::string&) &&;
79   /* Set a loader that displays the current value in help text. Optional. */
80   Flag& Getter(std::function<std::string()>) &;
81   Flag Getter(std::function<std::string()>) &&;
82   /* Set the callback for matches. The callback be invoked multiple times. */
83   Flag& Setter(std::function<bool(const FlagMatch&)>) &;
84   Flag Setter(std::function<bool(const FlagMatch&)>) &&;
85 
86   /* Examines a list of arguments, removing any matches from the list and
87    * invoking the `Setter` for every match. Returns `false` if the callback ever
88    * returns `false`. Non-matches are left in place. */
89   bool Parse(std::vector<std::string>& flags) const;
90   bool Parse(std::vector<std::string>&& flags) const;
91 
92   /* Write gflags `--helpxml` style output for a string-type flag. */
93   bool WriteGflagsCompatXml(std::ostream&) const;
94 
95  private:
96   /* Reports whether `Process` wants to consume zero, one, or two arguments. */
97   enum class FlagProcessResult {
98     /* Error in handling a flag, exit flag handling with an error result. */
99     kFlagError,
100     kFlagSkip,                  /* Flag skipped; consume no arguments. */
101     kFlagConsumed,              /* Flag processed; consume one argument. */
102     kFlagConsumedWithFollowing, /* Flag processed; consume 2 arguments. */
103     kFlagConsumedOnlyFollowing, /* Flag processed; consume next argument. */
104   };
105 
106   void ValidateAlias(const FlagAlias& alias);
107   Flag& UnvalidatedAlias(const FlagAlias& alias) &;
108   Flag UnvalidatedAlias(const FlagAlias& alias) &&;
109 
110   /* Attempt to match a single argument. */
111   FlagProcessResult Process(const std::string& argument,
112                             const std::optional<std::string>& next_arg) const;
113 
114   bool HasAlias(const FlagAlias&) const;
115 
116   friend std::ostream& operator<<(std::ostream&, const Flag&);
117   friend Flag InvalidFlagGuard();
118   friend Flag UnexpectedArgumentGuard();
119 
120   std::vector<FlagAlias> aliases_;
121   std::optional<std::string> help_;
122   std::optional<std::function<std::string()>> getter_;
123   std::optional<std::function<bool(const FlagMatch&)>> setter_;
124 };
125 
126 std::ostream& operator<<(std::ostream&, const Flag&);
127 
128 std::vector<std::string> ArgsToVec(int argc, char** argv);
129 
130 /* Handles a list of flags. Flags are matched in the order given in case two
131  * flags match the same argument. Matched flags are removed, leaving only
132  * unmatched arguments. */
133 bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>& args);
134 bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>&&);
135 
136 bool WriteGflagsCompatXml(const std::vector<Flag>&, std::ostream&);
137 
138 /* If any of these are used, they should be evaluated after all other flags, and
139  * in the order defined here (help before invalid flags, invalid flags before
140  * unexpected arguments). */
141 
142 /* If a "-help" or "--help" flag is present, prints all the flags and fails. */
143 Flag HelpFlag(const std::vector<Flag>& flags, const std::string& text = "");
144 /* Catches unrecognized arguments that begin with `-`, and errors out. This
145  * effectively denies unknown flags. */
146 Flag InvalidFlagGuard();
147 /* Catches any arguments not extracted by other Flag matchers and errors out.
148  * This effectively denies unknown flags and any positional arguments. */
149 Flag UnexpectedArgumentGuard();
150 
151 // Create a flag resembling a gflags argument of the given type. This includes
152 // "-[-]flag=*",support for all types, "-[-]noflag" support for booleans, and
153 // "-flag *", "--flag *", support for other types. The value passed in the flag
154 // is saved to the defined reference.
155 Flag GflagsCompatFlag(const std::string& name);
156 Flag GflagsCompatFlag(const std::string& name, std::string& value);
157 Flag GflagsCompatFlag(const std::string& name, std::int32_t& value);
158 Flag GflagsCompatFlag(const std::string& name, bool& value);
159 
160 }  // namespace cuttlefish
161