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 #include "common/libs/utils/result.h" 27 28 /* Support for parsing individual flags out of a larger list of flags. This 29 * supports externally determining the order that flags are evaluated in, and 30 * incrementally integrating with existing flag parsing implementations. 31 * 32 * Start with Flag() or one of the GflagsCompatFlag(...) functions to create new 33 * flags. These flags should be aggregated through the application through some 34 * other mechanism and then evaluated individually with Flag::Parse or together 35 * with ConsumeFlags on arguments. */ 36 37 namespace cuttlefish { 38 39 /* The matching behavior used with the name in `FlagAlias::name`. */ 40 enum class FlagAliasMode { 41 /* Match arguments of the form `<name><value>`. In practice, <name> usually 42 * looks like "-flag=" or "--flag=", where the "-" and "=" are included in 43 * parsing. */ 44 kFlagPrefix, 45 /* Match arguments of the form `<name>`. In practice, <name> will look like 46 * "-flag" or "--flag". */ 47 kFlagExact, 48 /* Match a pair of arguments of the form `<name>` `<value>`. In practice, 49 * <name> will look like "-flag" or "--flag". */ 50 kFlagConsumesFollowing, 51 /* Match a sequence of arguments of the form `<name>` `<value>` `<value>`. 52 * This uses heuristics to try to determine when `<value>` is actually another 53 * flag. */ 54 kFlagConsumesArbitrary, 55 }; 56 57 /* A single matching rule for a `Flag`. One `Flag` can have multiple rules. */ 58 struct FlagAlias { 59 FlagAliasMode mode; 60 std::string name; 61 }; 62 63 std::ostream& operator<<(std::ostream&, const FlagAlias&); 64 65 /* A successful match in an argument list from a `FlagAlias` inside a `Flag`. 66 * The `key` value corresponds to `FlagAlias::name`. For a match of 67 * `FlagAliasMode::kFlagExact`, `key` and `value` will both be the `name`. */ 68 struct FlagMatch { 69 std::string key; 70 std::string value; 71 }; 72 73 class Flag { 74 public: 75 /* Add an alias that triggers matches and calls to the `Setter` function. */ 76 Flag& Alias(const FlagAlias& alias) &; 77 Flag Alias(const FlagAlias& alias) &&; 78 /* Set help text, visible in the class ostream writer method. Optional. */ 79 Flag& Help(const std::string&) &; 80 Flag Help(const std::string&) &&; 81 /* Set a loader that displays the current value in help text. Optional. */ 82 Flag& Getter(std::function<std::string()>) &; 83 Flag Getter(std::function<std::string()>) &&; 84 /* Set the callback for matches. The callback may be invoked multiple times. 85 */ 86 Flag& Setter(std::function<Result<void>(const FlagMatch&)>) &; 87 Flag Setter(std::function<Result<void>(const FlagMatch&)>) &&; 88 89 /* Examines a list of arguments, removing any matches from the list and 90 * invoking the `Setter` for every match. Returns `false` if the callback ever 91 * returns `false`. Non-matches are left in place. */ 92 Result<void> Parse(std::vector<std::string>& flags) const; 93 Result<void> Parse(std::vector<std::string>&& flags) const; 94 95 /* Write gflags `--helpxml` style output for a string-type flag. */ 96 bool WriteGflagsCompatXml(std::ostream&) const; 97 98 private: 99 /* Reports whether `Process` wants to consume zero, one, or two arguments. */ 100 enum class FlagProcessResult { 101 /* Error in handling a flag, exit flag handling with an error result. */ 102 kFlagSkip, /* Flag skipped; consume no arguments. */ 103 kFlagConsumed, /* Flag processed; consume one argument. */ 104 kFlagConsumedWithFollowing, /* Flag processed; consume 2 arguments. */ 105 kFlagConsumedOnlyFollowing, /* Flag processed; consume next argument. */ 106 }; 107 108 void ValidateAlias(const FlagAlias& alias); 109 Flag& UnvalidatedAlias(const FlagAlias& alias) &; 110 Flag UnvalidatedAlias(const FlagAlias& alias) &&; 111 112 /* Attempt to match a single argument. */ 113 Result<FlagProcessResult> Process( 114 const std::string& argument, 115 const std::optional<std::string>& next_arg) const; 116 117 bool HasAlias(const FlagAlias&) const; 118 119 friend std::ostream& operator<<(std::ostream&, const Flag&); 120 friend Flag InvalidFlagGuard(); 121 friend Flag UnexpectedArgumentGuard(); 122 123 std::vector<FlagAlias> aliases_; 124 std::optional<std::string> help_; 125 std::optional<std::function<std::string()>> getter_; 126 std::optional<std::function<Result<void>(const FlagMatch&)>> setter_; 127 }; 128 129 std::ostream& operator<<(std::ostream&, const Flag&); 130 131 std::vector<std::string> ArgsToVec(int argc, char** argv); 132 133 Result<bool> ParseBool(const std::string& value, const std::string& name); 134 135 /* Handles a list of flags. Flags are matched in the order given in case two 136 * flags match the same argument. Matched flags are removed, leaving only 137 * unmatched arguments. */ 138 Result<void> ConsumeFlags(const std::vector<Flag>& flags, 139 std::vector<std::string>& args, 140 const bool recognize_end_of_option_mark = false); 141 Result<void> ConsumeFlags(const std::vector<Flag>& flags, 142 std::vector<std::string>&&, 143 const bool recognize_end_of_option_mark = false); 144 145 bool WriteGflagsCompatXml(const std::vector<Flag>&, std::ostream&); 146 147 /* If -verbosity or --verbosity flags have a value, translates it to an android 148 * LogSeverity */ 149 Flag VerbosityFlag(android::base::LogSeverity& value); 150 151 /* If any of these are used, they should be evaluated after all other flags, and 152 * in the order defined here (help before invalid flags, invalid flags before 153 * unexpected arguments). */ 154 155 /* If a "-help" or "--help" flag is present, prints all the flags and fails. */ 156 Flag HelpFlag(const std::vector<Flag>& flags, std::string text = ""); 157 158 /* If a "-helpxml" is present, prints all the flags in XML and fails. */ 159 Flag HelpXmlFlag(const std::vector<Flag>& flags, std::ostream&, bool& value, 160 std::string text = ""); 161 162 /* Catches unrecognized arguments that begin with `-`, and errors out. This 163 * effectively denies unknown flags. */ 164 Flag InvalidFlagGuard(); 165 /* Catches any arguments not extracted by other Flag matchers and errors out. 166 * This effectively denies unknown flags and any positional arguments. */ 167 Flag UnexpectedArgumentGuard(); 168 169 // Create a flag resembling a gflags argument of the given type. This includes 170 // "-[-]flag=*",support for all types, "-[-]noflag" support for booleans, and 171 // "-flag *", "--flag *", support for other types. The value passed in the flag 172 // is saved to the defined reference. 173 Flag GflagsCompatFlag(const std::string& name); 174 Flag GflagsCompatFlag(const std::string& name, std::string& value); 175 Flag GflagsCompatFlag(const std::string& name, std::int32_t& value); 176 Flag GflagsCompatFlag(const std::string& name, bool& value); 177 Flag GflagsCompatFlag(const std::string& name, std::vector<std::string>& value); 178 Flag GflagsCompatFlag(const std::string& name, std::vector<bool>& value, 179 const bool default_value); 180 181 } // namespace cuttlefish 182