• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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