1 /* 2 * Copyright (C) 2023 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 <memory> 22 #include <optional> 23 #include <string> 24 #include <unordered_map> 25 #include <variant> 26 #include <vector> 27 28 #include "common/libs/utils/contains.h" 29 #include "common/libs/utils/flag_parser.h" 30 #include "common/libs/utils/result.h" 31 #include "host/commands/cvd/types.h" 32 33 namespace cuttlefish { 34 35 /** 36 * Data structure to represent cvd user-facing flags 37 * 38 * Flag in flag_parser.h is more on parsing. gflags library would be 39 * slowly depreicated. The cvd driver and selector flags are a specification for 40 * a user-facing flag. 41 */ 42 template <typename T> 43 class CvdFlag { 44 public: 45 using GflagFactoryCallback = 46 std::function<Flag(const std::string& name, T& value_out)>; CvdFlag(const std::string & name)47 CvdFlag(const std::string& name) 48 : name_(name), 49 gflag_factory_cb([](const std::string& name, T& value_out) { 50 return GflagsCompatFlag(name, value_out); 51 }) {} 52 CvdFlag(const std::string & name,const T & default_value)53 CvdFlag(const std::string& name, const T& default_value) 54 : name_(name), 55 default_value_(default_value), 56 gflag_factory_cb([](const std::string& name, T& value_out) { 57 return GflagsCompatFlag(name, value_out); 58 }) {} 59 Name()60 std::string Name() const { return name_; } HelpMessage()61 std::string HelpMessage() const { return help_msg_; } SetHelpMessage(const std::string & help_msg)62 CvdFlag& SetHelpMessage(const std::string& help_msg) & { 63 help_msg_ = help_msg; 64 return *this; 65 } SetHelpMessage(const std::string & help_msg)66 CvdFlag SetHelpMessage(const std::string& help_msg) && { 67 help_msg_ = help_msg; 68 return *this; 69 } HasDefaultValue()70 bool HasDefaultValue() const { return default_value_ != std::nullopt; } DefaultValue()71 Result<T> DefaultValue() const { 72 CF_EXPECT(HasDefaultValue()); 73 return *default_value_; 74 } 75 SetGflagFactory(GflagFactoryCallback factory)76 CvdFlag& SetGflagFactory(GflagFactoryCallback factory) & { 77 gflag_factory_cb = std::move(factory); 78 return *this; 79 } SetGflagFactory(GflagFactoryCallback factory)80 CvdFlag SetGflagFactory(GflagFactoryCallback factory) && { 81 gflag_factory_cb = std::move(factory); 82 return *this; 83 } 84 85 // returns CF_ERR if parsing error, 86 // returns std::nullopt if parsing was okay but the flag wasn't given FilterFlag(cvd_common::Args & args)87 Result<std::optional<T>> FilterFlag(cvd_common::Args& args) const { 88 const int args_initial_size = args.size(); 89 if (args_initial_size == 0) { 90 return std::nullopt; 91 } 92 T value; 93 CF_EXPECT(ParseFlags({gflag_factory_cb(name_, value)}, args), 94 "Failed to parse --" << name_); 95 if (args.size() == args_initial_size) { 96 // not consumed 97 return std::nullopt; 98 } 99 return value; 100 } 101 102 // Parses the arguments. If flag is given, returns the parsed value. If not, 103 // returns the default value if any. If no default value, it returns CF_ERR. CalculateFlag(cvd_common::Args & args)104 Result<T> CalculateFlag(cvd_common::Args& args) const { 105 auto value_opt = CF_EXPECT(FilterFlag(args)); 106 if (!value_opt) { 107 CF_EXPECT(default_value_ != std::nullopt); 108 value_opt = default_value_; 109 } 110 return *value_opt; 111 } 112 113 private: 114 const std::string name_; 115 std::string help_msg_; 116 std::optional<T> default_value_; 117 /** 118 * A callback function to generate Flag defined in 119 * common/libs/utils/flag_parser.h. The name is this CvdFlag's name. 120 * The value is a buffer that is kept in this object 121 */ 122 GflagFactoryCallback gflag_factory_cb; 123 }; 124 125 class CvdFlagProxy { 126 friend class FlagCollection; 127 128 public: 129 enum class FlagType : std::uint32_t { 130 kUnknown = 0, 131 kBool, 132 kInt32, 133 kString, 134 }; 135 ToString(const FlagType flag_type)136 static std::string ToString(const FlagType flag_type) { 137 switch (flag_type) { 138 case FlagType::kUnknown: 139 return "kUnknown"; 140 case FlagType::kBool: 141 return "bool"; 142 case FlagType::kInt32: 143 return "std::int32_t"; 144 case FlagType::kString: 145 return "std::string"; 146 } 147 } 148 149 template <typename T> CvdFlagProxy(CvdFlag<T> && flag)150 CvdFlagProxy(CvdFlag<T>&& flag) : flag_{std::move(flag)} {} 151 152 template <typename T> GetFlag()153 const CvdFlag<T>* GetFlag() const { 154 return std::get_if<CvdFlag<T>>(&flag_); 155 } 156 157 template <typename T> GetFlag()158 CvdFlag<T>* GetFlag() { 159 return std::get_if<CvdFlag<T>>(&flag_); 160 } 161 162 /* 163 * If the actual type of flag_ is not handled by SelectorFlagProxy, it is a 164 * developer error, and the Name() and HasDefaultValue() will returns 165 * CF_ERR 166 */ 167 Result<std::string> Name() const; 168 Result<bool> HasDefaultValue() const; 169 170 FlagType GetType() const; 171 172 template <typename T> DefaultValue()173 Result<T> DefaultValue() const { 174 const bool has_default_value = CF_EXPECT(HasDefaultValue()); 175 CF_EXPECT(has_default_value == true); 176 const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); 177 CF_EXPECT(ptr != nullptr); 178 return ptr->DefaultValue(); 179 } 180 181 // returns CF_ERR if parsing error, 182 // returns std::nullopt if parsing was okay but the flag wasn't given 183 template <typename T> FilterFlag(cvd_common::Args & args)184 Result<std::optional<T>> FilterFlag(cvd_common::Args& args) const { 185 std::optional<T> output; 186 const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); 187 CF_EXPECT(ptr != nullptr); 188 output = CF_EXPECT(ptr->FilterFlag(args)); 189 return output; 190 } 191 192 // Parses the arguments. If flag is given, returns the parsed value. If not, 193 // returns the default value if any. If no default value, it returns CF_ERR. 194 template <typename T> CalculateFlag(cvd_common::Args & args)195 Result<T> CalculateFlag(cvd_common::Args& args) const { 196 bool has_default_value = CF_EXPECT(HasDefaultValue()); 197 CF_EXPECT(has_default_value == true); 198 const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); 199 CF_EXPECT(ptr != nullptr); 200 T output = CF_EXPECT(ptr->CalculateFlag(args)); 201 return output; 202 } 203 204 using ValueVariant = std::variant<std::int32_t, bool, std::string>; 205 206 // Returns std::nullopt when the parsing goes okay but the flag wasn't given 207 // Returns ValueVariant when the flag was given in args 208 // Returns CF_ERR when the parsing failed or the type is not supported 209 Result<std::optional<ValueVariant>> FilterFlag(cvd_common::Args& args) const; 210 211 private: 212 std::variant<CvdFlag<std::int32_t>, CvdFlag<bool>, CvdFlag<std::string>> 213 flag_; 214 }; 215 216 class FlagCollection { 217 public: 218 using ValueVariant = CvdFlagProxy::ValueVariant; 219 EnrollFlag(CvdFlagProxy && flag)220 Result<void> EnrollFlag(CvdFlagProxy&& flag) { 221 auto name = CF_EXPECT(flag.Name()); 222 CF_EXPECT(!Contains(name_flag_map_, name), 223 name << " is already registered."); 224 name_flag_map_.emplace(name, std::move(flag)); 225 return {}; 226 } 227 228 template <typename T> EnrollFlag(CvdFlag<T> && flag)229 Result<void> EnrollFlag(CvdFlag<T>&& flag) { 230 CF_EXPECT(EnrollFlag(CvdFlagProxy(std::move(flag)))); 231 return {}; 232 } 233 GetFlag(const std::string & name)234 Result<CvdFlagProxy> GetFlag(const std::string& name) const { 235 const auto itr = name_flag_map_.find(name); 236 CF_EXPECT(itr != name_flag_map_.end(), 237 "Flag \"" << name << "\" is not found."); 238 const CvdFlagProxy& flag_proxy = itr->second; 239 return flag_proxy; 240 } 241 242 std::vector<CvdFlagProxy> Flags() const; 243 244 struct FlagValuePair { 245 ValueVariant value; 246 CvdFlagProxy flag; 247 }; 248 249 /* does not consider default values 250 * so, if not default value and the flag wasn't given, it won't be found 251 * in the returned map 252 */ 253 Result<std::unordered_map<std::string, FlagValuePair>> FilterFlags( 254 cvd_common::Args& args) const; 255 256 /* considers default values 257 * so, if the flag wasn't given, the default value will be used to fill 258 * out the returned map. If a default value isn't available and the flag 259 * isn't given either, the entry won't be in the returned map 260 */ 261 Result<std::unordered_map<std::string, FlagValuePair>> CalculateFlags( 262 cvd_common::Args& args) const; 263 264 template <typename T> GetValue(const ValueVariant & value_variant)265 static Result<T> GetValue(const ValueVariant& value_variant) { 266 auto* value_ptr = std::get_if<T>(std::addressof(value_variant)); 267 CF_EXPECT(value_ptr != nullptr, 268 "GetValue template function was instantiated with a wrong type."); 269 return *value_ptr; 270 } 271 272 template <typename T> GetValue(const FlagValuePair & flag_and_value)273 static Result<T> GetValue(const FlagValuePair& flag_and_value) { 274 std::string flag_type_string = 275 CvdFlagProxy::ToString(flag_and_value.flag.GetType()); 276 auto* value_ptr = std::get_if<T>(std::addressof(flag_and_value.value)); 277 CF_EXPECT(value_ptr != nullptr, 278 "The actual flag type is " << flag_type_string); 279 return *value_ptr; 280 } 281 282 private: 283 std::unordered_map<std::string, CvdFlagProxy> name_flag_map_; 284 }; 285 286 } // namespace cuttlefish 287