| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_map> |
| #include <variant> |
| #include <vector> |
| |
| #include "common/libs/utils/contains.h" |
| #include "common/libs/utils/flag_parser.h" |
| #include "common/libs/utils/result.h" |
| #include "host/commands/cvd/types.h" |
| |
| namespace cuttlefish { |
| |
| /** |
| * Data structure to represent cvd user-facing flags |
| * |
| * Flag in flag_parser.h is more on parsing. gflags library would be |
| * slowly depreicated. The cvd driver and selector flags are a specification for |
| * a user-facing flag. |
| */ |
| template <typename T> |
| class CvdFlag { |
| public: |
| using GflagFactoryCallback = |
| std::function<Flag(const std::string& name, T& value_out)>; |
| CvdFlag(const std::string& name) |
| : name_(name), |
| gflag_factory_cb([](const std::string& name, T& value_out) { |
| return GflagsCompatFlag(name, value_out); |
| }) {} |
| |
| CvdFlag(const std::string& name, const T& default_value) |
| : name_(name), |
| default_value_(default_value), |
| gflag_factory_cb([](const std::string& name, T& value_out) { |
| return GflagsCompatFlag(name, value_out); |
| }) {} |
| |
| std::string Name() const { return name_; } |
| std::string HelpMessage() const { return help_msg_; } |
| CvdFlag& SetHelpMessage(const std::string& help_msg) & { |
| help_msg_ = help_msg; |
| return *this; |
| } |
| CvdFlag SetHelpMessage(const std::string& help_msg) && { |
| help_msg_ = help_msg; |
| return *this; |
| } |
| bool HasDefaultValue() const { return default_value_ != std::nullopt; } |
| Result<T> DefaultValue() const { |
| CF_EXPECT(HasDefaultValue()); |
| return *default_value_; |
| } |
| |
| CvdFlag& SetGflagFactory(GflagFactoryCallback factory) & { |
| gflag_factory_cb = std::move(factory); |
| return *this; |
| } |
| CvdFlag SetGflagFactory(GflagFactoryCallback factory) && { |
| gflag_factory_cb = std::move(factory); |
| return *this; |
| } |
| |
| // returns CF_ERR if parsing error, |
| // returns std::nullopt if parsing was okay but the flag wasn't given |
| Result<std::optional<T>> FilterFlag(cvd_common::Args& args) const { |
| const int args_initial_size = args.size(); |
| if (args_initial_size == 0) { |
| return std::nullopt; |
| } |
| T value; |
| CF_EXPECT(ParseFlags({gflag_factory_cb(name_, value)}, args), |
| "Failed to parse --" << name_); |
| if (args.size() == args_initial_size) { |
| // not consumed |
| return std::nullopt; |
| } |
| return value; |
| } |
| |
| // Parses the arguments. If flag is given, returns the parsed value. If not, |
| // returns the default value if any. If no default value, it returns CF_ERR. |
| Result<T> CalculateFlag(cvd_common::Args& args) const { |
| auto value_opt = CF_EXPECT(FilterFlag(args)); |
| if (!value_opt) { |
| CF_EXPECT(default_value_ != std::nullopt); |
| value_opt = default_value_; |
| } |
| return *value_opt; |
| } |
| |
| private: |
| const std::string name_; |
| std::string help_msg_; |
| std::optional<T> default_value_; |
| /** |
| * A callback function to generate Flag defined in |
| * common/libs/utils/flag_parser.h. The name is this CvdFlag's name. |
| * The value is a buffer that is kept in this object |
| */ |
| GflagFactoryCallback gflag_factory_cb; |
| }; |
| |
| class CvdFlagProxy { |
| friend class FlagCollection; |
| |
| public: |
| enum class FlagType : std::uint32_t { |
| kUnknown = 0, |
| kBool, |
| kInt32, |
| kString, |
| }; |
| |
| static std::string ToString(const FlagType flag_type) { |
| switch (flag_type) { |
| case FlagType::kUnknown: |
| return "kUnknown"; |
| case FlagType::kBool: |
| return "bool"; |
| case FlagType::kInt32: |
| return "std::int32_t"; |
| case FlagType::kString: |
| return "std::string"; |
| } |
| } |
| |
| template <typename T> |
| CvdFlagProxy(CvdFlag<T>&& flag) : flag_{std::move(flag)} {} |
| |
| template <typename T> |
| const CvdFlag<T>* GetFlag() const { |
| return std::get_if<CvdFlag<T>>(&flag_); |
| } |
| |
| template <typename T> |
| CvdFlag<T>* GetFlag() { |
| return std::get_if<CvdFlag<T>>(&flag_); |
| } |
| |
| /* |
| * If the actual type of flag_ is not handled by SelectorFlagProxy, it is a |
| * developer error, and the Name() and HasDefaultValue() will returns |
| * CF_ERR |
| */ |
| Result<std::string> Name() const; |
| Result<bool> HasDefaultValue() const; |
| |
| FlagType GetType() const; |
| |
| template <typename T> |
| Result<T> DefaultValue() const { |
| const bool has_default_value = CF_EXPECT(HasDefaultValue()); |
| CF_EXPECT(has_default_value == true); |
| const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); |
| CF_EXPECT(ptr != nullptr); |
| return ptr->DefaultValue(); |
| } |
| |
| // returns CF_ERR if parsing error, |
| // returns std::nullopt if parsing was okay but the flag wasn't given |
| template <typename T> |
| Result<std::optional<T>> FilterFlag(cvd_common::Args& args) const { |
| std::optional<T> output; |
| const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); |
| CF_EXPECT(ptr != nullptr); |
| output = CF_EXPECT(ptr->FilterFlag(args)); |
| return output; |
| } |
| |
| // Parses the arguments. If flag is given, returns the parsed value. If not, |
| // returns the default value if any. If no default value, it returns CF_ERR. |
| template <typename T> |
| Result<T> CalculateFlag(cvd_common::Args& args) const { |
| bool has_default_value = CF_EXPECT(HasDefaultValue()); |
| CF_EXPECT(has_default_value == true); |
| const auto* ptr = CF_EXPECT(std::get_if<CvdFlag<T>>(&flag_)); |
| CF_EXPECT(ptr != nullptr); |
| T output = CF_EXPECT(ptr->CalculateFlag(args)); |
| return output; |
| } |
| |
| using ValueVariant = std::variant<std::int32_t, bool, std::string>; |
| |
| // Returns std::nullopt when the parsing goes okay but the flag wasn't given |
| // Returns ValueVariant when the flag was given in args |
| // Returns CF_ERR when the parsing failed or the type is not supported |
| Result<std::optional<ValueVariant>> FilterFlag(cvd_common::Args& args) const; |
| |
| private: |
| std::variant<CvdFlag<std::int32_t>, CvdFlag<bool>, CvdFlag<std::string>> |
| flag_; |
| }; |
| |
| class FlagCollection { |
| public: |
| using ValueVariant = CvdFlagProxy::ValueVariant; |
| |
| Result<void> EnrollFlag(CvdFlagProxy&& flag) { |
| auto name = CF_EXPECT(flag.Name()); |
| CF_EXPECT(!Contains(name_flag_map_, name), |
| name << " is already registered."); |
| name_flag_map_.emplace(name, std::move(flag)); |
| return {}; |
| } |
| |
| template <typename T> |
| Result<void> EnrollFlag(CvdFlag<T>&& flag) { |
| CF_EXPECT(EnrollFlag(CvdFlagProxy(std::move(flag)))); |
| return {}; |
| } |
| |
| Result<CvdFlagProxy> GetFlag(const std::string& name) const { |
| const auto itr = name_flag_map_.find(name); |
| CF_EXPECT(itr != name_flag_map_.end(), |
| "Flag \"" << name << "\" is not found."); |
| const CvdFlagProxy& flag_proxy = itr->second; |
| return flag_proxy; |
| } |
| |
| std::vector<CvdFlagProxy> Flags() const; |
| |
| struct FlagValuePair { |
| CvdFlagProxy flag; |
| ValueVariant value; |
| }; |
| |
| /* does not consider default values |
| * so, if not default value and the flag wasn't given, it won't be found |
| * in the returned map |
| */ |
| Result<std::unordered_map<std::string, FlagValuePair>> FilterFlags( |
| cvd_common::Args& args) const; |
| |
| /* considers default values |
| * so, if the flag wasn't given, the default value will be used to fill |
| * out the returned map. If a default value isn't available and the flag |
| * isn't given either, the entry won't be in the returned map |
| */ |
| Result<std::unordered_map<std::string, FlagValuePair>> CalculateFlags( |
| cvd_common::Args& args) const; |
| |
| template <typename T> |
| static Result<T> GetValue(const ValueVariant& value_variant) { |
| auto* value_ptr = std::get_if<T>(std::addressof(value_variant)); |
| CF_EXPECT(value_ptr != nullptr, |
| "GetValue template function was instantiated with a wrong type."); |
| return *value_ptr; |
| } |
| |
| template <typename T> |
| static Result<T> GetValue(const FlagValuePair& flag_and_value) { |
| std::string flag_type_string = |
| CvdFlagProxy::ToString(flag_and_value.flag.GetType()); |
| auto* value_ptr = std::get_if<T>(std::addressof(flag_and_value.value)); |
| CF_EXPECT(value_ptr != nullptr, |
| "The actual flag type is " << flag_type_string); |
| return *value_ptr; |
| } |
| |
| private: |
| std::unordered_map<std::string, CvdFlagProxy> name_flag_map_; |
| }; |
| |
| } // namespace cuttlefish |