#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace argument_parser::v2 { namespace internal { static inline fake_parser fake_parser{}; } enum class add_argument_flags { ShortArgument, LongArgument, HelpText, Action, Required }; namespace flags { constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument; constexpr static inline add_argument_flags LongArgument = add_argument_flags::LongArgument; constexpr static inline add_argument_flags HelpText = add_argument_flags::HelpText; constexpr static inline add_argument_flags Action = add_argument_flags::Action; constexpr static inline add_argument_flags Required = add_argument_flags::Required; } // namespace flags class base_parser : private argument_parser::base_parser { public: template using typed_flag_value = std::variant, bool>; using non_typed_flag_value = std::variant; template using typed_argument_pair = std::pair>; using non_typed_argument_pair = std::pair; template void add_argument(std::unordered_map> const &argument_pairs) { add_argument_impl, T>(argument_pairs); } template void add_argument(std::initializer_list> const &pairs) { std::unordered_map> args; for (auto &[k, v] : pairs) { args[k] = v; } add_argument(args); } void add_argument(std::initializer_list const &pairs) { std::unordered_map args; for (auto &[k, v] : pairs) { args[k] = v; } add_argument(args); } void add_argument(std::unordered_map const &argument_pairs) { add_argument_impl(argument_pairs); } argument_parser::base_parser &to_v1() { return *this; } void handle_arguments(std::initializer_list convention_types) { base::handle_arguments(convention_types); } template std::optional get_optional(std::string const &arg) { return base::get_optional(arg); } void on_complete(std::function const &action) { base::on_complete(action); } protected: void set_program_name(std::string p) { base::program_name = std::move(p); } std::vector &ref_parsed_args() { return base::parsed_arguments; } private: template void add_argument_impl(ArgsMap const &argument_pairs) { std::unordered_map found_params{ {extended_add_argument_flags::IsTyped, IsTyped}}; std::string short_arg, long_arg, help_text; std::unique_ptr action; bool required = false; if (argument_pairs.find(add_argument_flags::ShortArgument) != argument_pairs.end()) { found_params[extended_add_argument_flags::ShortArgument] = true; short_arg = get_or_throw(argument_pairs.at(add_argument_flags::ShortArgument), "short"); } if (argument_pairs.find(add_argument_flags::LongArgument) != argument_pairs.end()) { found_params[extended_add_argument_flags::LongArgument] = true; long_arg = get_or_throw(argument_pairs.at(add_argument_flags::LongArgument), "long"); if (short_arg.empty()) short_arg = long_arg; } else { if (!short_arg.empty()) long_arg = short_arg; } if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) { found_params[extended_add_argument_flags::Action] = true; action = get_or_throw(argument_pairs.at(add_argument_flags::Action), "action").clone(); } if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) { help_text = get_or_throw(argument_pairs.at(add_argument_flags::HelpText), "help"); } else { help_text = short_arg + ", " + long_arg; } if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() && get_or_throw(argument_pairs.at(add_argument_flags::Required), "required")) { required = true; } auto suggested_add = suggest_candidate(found_params); if (suggested_add == candidate_type::unknown) { throw std::runtime_error("Could not match any add argument overload to given parameters. Are you " "missing some required parameter?"); } if constexpr (IsTyped) { switch (suggested_add) { case candidate_type::typed_action: base::add_argument(short_arg, long_arg, help_text, *static_cast(&(*action)), required); break; case candidate_type::store_other: base::add_argument(short_arg, long_arg, help_text, required); break; default: throw std::runtime_error("Could not match the arguments against any overload."); } } else { switch (suggested_add) { case candidate_type::non_typed_action: base::add_argument(short_arg, long_arg, help_text, *static_cast(&(*action)), required); break; case candidate_type::store_boolean: base::add_argument(short_arg, long_arg, help_text, required); break; default: throw std::runtime_error( "Could not match the arguments against any overload. The suggested candidate was: " + std::to_string((int(suggested_add)))); } } } using base = argument_parser::base_parser; enum class extended_add_argument_flags { ShortArgument, LongArgument, Action, IsTyped }; enum class candidate_type { store_boolean, store_other, typed_action, non_typed_action, unknown }; template bool satisfies_at_least_one(std::array const &arr, std::unordered_map const &map) { for (const auto &req : arr) { if (map.find(req) != map.end()) return true; } return false; } candidate_type suggest_candidate(std::unordered_map const &available_vars) { auto constexpr required_at_least_one = std::array{ extended_add_argument_flags::ShortArgument, extended_add_argument_flags::LongArgument}; if (!satisfies_at_least_one(required_at_least_one, available_vars)) return candidate_type::unknown; if (available_vars.find(extended_add_argument_flags::Action) != available_vars.end()) { if (available_vars.at(extended_add_argument_flags::IsTyped)) return candidate_type::typed_action; else return candidate_type::non_typed_action; } if (available_vars.at(extended_add_argument_flags::IsTyped)) return candidate_type::store_other; return candidate_type::store_boolean; } template T get_or_throw(typed_flag_value const &v, std::string_view key) { if (auto p = std::get_if(&v)) return *p; throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key)); } template T get_or_throw(non_typed_flag_value const &v, std::string_view key) { if (auto p = std::get_if(&v)) return *p; throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key)); } }; } // namespace argument_parser::v2