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