diff --git a/include/argparse.hpp b/include/argparse.hpp index b6de356..efa8721 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -626,25 +627,85 @@ namespace argparse std::unique_ptr type_handler = std::make_unique>(); }; - class OptionsHolder + class ArgumentCommonImpl { - protected: - explicit OptionsHolder(Options options) + public: + ArgumentCommonImpl(Options options, std::function name_for_error) : m_options(std::move(options)) + , m_name_for_error(name_for_error) + { + } + + auto get_name() const -> std::string const & { + return m_options.names.front(); } - ~OptionsHolder() = default; auto get_names() const -> std::vector const & { return m_options.names; } + auto get_joined_names() const -> std::string + { + return join(m_options.names, "/"); + } + + auto has_nargs() const -> bool + { + return m_options.nargs.has_value(); + } + + auto has_nargs_number() const -> bool + { + return std::holds_alternative(*m_options.nargs); + } + + auto get_nargs_number() const -> std::size_t + { + return std::get(*m_options.nargs); + } + + auto get_nargs_option() const -> Nargs + { + return std::get(*m_options.nargs); + } + + auto is_mutually_exclusive() const -> bool + { + return m_options.mutually_exclusive_group != nullptr; + } + + auto is_mutually_exclusive_with(ArgumentCommonImpl const & other) const -> bool + { + return (m_options.mutually_exclusive_group != nullptr) && (m_options.mutually_exclusive_group == other.m_options.mutually_exclusive_group); + } + + auto expects_argument() const -> bool + { + return m_options.action == store || m_options.action == append; + } + + auto has_version_action() const -> bool + { + return m_options.action == version; + } + auto get_help() const -> std::string const & { return m_options.help; } + auto get_default() const -> std::any const & + { + return m_options.default_; + } + + auto get_const() const -> std::any const & + { + return m_options.const_; + } + auto get_metavar() const -> std::string const & { return m_options.metavar; @@ -655,210 +716,259 @@ namespace argparse return m_options.dest; } + auto get_required() const -> bool + { + return m_options.required; + } + auto get_action() const -> Action { return m_options.action; } - auto get_const() const -> std::any const & + auto has_choices() const -> bool { - return m_options.const_; + return !m_options.choices.empty(); } - auto get_default() const -> std::any const & + auto get_joined_choices(std::string_view separator) const -> std::string { - return m_options.default_; + return join(m_options.choices | std::views::transform([&](auto const & choice) { return m_options.type_handler->to_string(choice); }), separator); } - auto get_required() const -> bool + auto parse_arguments(std::ranges::view auto tokens) -> std::any { - return m_options.required; + auto const values = consume_tokens(tokens); + return m_options.type_handler->transform(values); } - auto get_choices() const -> std::vector const & + auto consume_token(Token & token) const -> std::any { - return m_options.choices; + token.m_consumed = true; + return process_token(token.m_token); } - auto get_nargs() const -> std::optional> const & + auto process_token(std::string const & token) const -> std::any { - return m_options.nargs; + auto const value = m_options.type_handler->from_string(token); + if (!value.has_value()) + { + throw parsing_error(std::format("argument {}: invalid value: '{}'", m_name_for_error(), token)); + } + check_choices(value); + return value; } - auto get_mutually_exclusive_group() const -> MutuallyExclusiveGroup const * + auto consume_tokens(std::ranges::view auto tokens) const -> std::vector { - return m_options.mutually_exclusive_group; + auto result = std::vector(); + auto consumed = std::vector(); + for (auto & token : tokens) + { + result.push_back(process_token(token.m_token)); + consumed.push_back(&token); + } + std::ranges::for_each(consumed, [](auto token) { token->m_consumed = true; }); + return result; } - auto get_type_handler() const -> TypeHandler const & + auto check_choices(std::any const & value) const -> void { - return *m_options.type_handler; + if (m_options.choices.empty()) + { + return; + } + + if (!std::ranges::any_of( + m_options.choices, + [&](auto const & rhs) { return m_options.type_handler->compare(value, rhs); })) + { + auto const message = std::format( + "argument {}: invalid choice: {} (choose from {})", + get_joined_names(), + m_options.type_handler->to_string(value), + get_joined_choices(", ")); + throw parsing_error(message); + } + } + + auto get_transformed(std::vector const & values) const -> std::any + { + return m_options.type_handler->transform(values); + } + + auto get_size(std::any const & value) const -> std::size_t + { + return m_options.type_handler->size(value); + } + + auto append_value(std::any const & value, std::any & values) const -> void + { + m_options.type_handler->append(value, values); } private: Options m_options; + std::function m_name_for_error; }; - class ArgumentCommon : public Argument, public Formattable, public OptionsHolder + class ArgumentCommonBase : public Argument, public Formattable { public: - explicit ArgumentCommon(Options options) - : OptionsHolder(std::move(options)) + explicit ArgumentCommonBase(Options options) + : m_impl(std::move(options), [this]() { return this->get_name_for_error(); }) { } - virtual ~ArgumentCommon() = default; + virtual ~ArgumentCommonBase() = default; - auto get_name() const -> std::string const & + auto get_name() const -> std::string const & override { - return get_names().front(); + return m_impl.get_name(); } - auto get_names() const -> std::vector const & + auto get_names() const -> std::vector const & override { - return OptionsHolder::get_names(); + return m_impl.get_names(); } - auto get_joined_names() const -> std::string + auto is_mutually_exclusive() const -> bool override { - return join(get_names(), "/"); + return m_impl.is_mutually_exclusive(); } - auto has_nargs() const -> bool + auto is_mutually_exclusive_with(Argument const & other) const -> bool override { - return get_nargs().has_value(); + return m_impl.is_mutually_exclusive_with(static_cast(other).m_impl); } - auto has_nargs_number() const -> bool + auto is_mutually_exclusive_with(Formattable const & other) const -> bool override { - return std::holds_alternative(*get_nargs()); + return m_impl.is_mutually_exclusive_with(static_cast(other).m_impl); } - auto get_nargs_number() const -> std::size_t + auto expects_argument() const -> bool override { - return std::get(*get_nargs()); + return m_impl.expects_argument(); } - auto get_nargs_option() const -> Nargs + auto get_joined_names() const -> std::string override { - return std::get(*get_nargs()); + return m_impl.get_joined_names(); } - auto is_mutually_exclusive() const -> bool + auto get_help() const -> std::string const & override { - return get_mutually_exclusive_group() != nullptr; + return m_impl.get_help(); } - auto is_mutually_exclusive_with(Argument const & other) const -> bool + auto has_nargs() const -> bool override { - return (get_mutually_exclusive_group() != nullptr) && (get_mutually_exclusive_group() == static_cast(other).get_mutually_exclusive_group()); + return m_impl.has_nargs(); } - auto is_mutually_exclusive_with(Formattable const & other) const -> bool + auto has_nargs_number() const -> bool override { - return (get_mutually_exclusive_group() != nullptr) && (get_mutually_exclusive_group() == static_cast(other).get_mutually_exclusive_group()); + return m_impl.has_nargs_number(); } - auto expects_argument() const -> bool + auto has_choices() const -> bool override { - return get_action() == store || get_action() == append; + return m_impl.has_choices(); } - auto has_version_action() const -> bool + auto get_joined_choices(std::string_view separator) const -> std::string override { - return get_action() == version; + return m_impl.get_joined_choices(separator); } - auto get_help() const -> std::string const & + auto get_nargs_number() const -> std::size_t override { - return OptionsHolder::get_help(); + return m_impl.get_nargs_number(); } - auto has_choices() const -> bool + auto get_nargs_option() const -> Nargs override { - return !get_choices().empty(); + return m_impl.get_nargs_option(); } - auto get_joined_choices(std::string_view separator) const -> std::string + protected: + auto get_default() const -> std::any const & { - return join(get_choices() | std::views::transform([&](auto const & choice) { return get_type_handler().to_string(choice); }), separator); + return m_impl.get_default(); } - protected: - virtual auto get_name_for_error() const -> std::string = 0; + auto get_const() const -> std::any const & + { + return m_impl.get_const(); + } + + auto get_metavar() const -> std::string const & + { + return m_impl.get_metavar(); + } + + auto get_dest() const -> std::string const & + { + return m_impl.get_dest(); + } + + auto get_required() const -> bool + { + return m_impl.get_required(); + } + + auto get_action() const -> Action + { + return m_impl.get_action(); + } auto parse_arguments(std::ranges::view auto tokens) -> std::any { - auto const values = consume_tokens(tokens); - return get_type_handler().transform(values); + return m_impl.parse_arguments(tokens); } auto consume_token(Token & token) const -> std::any { - token.m_consumed = true; - return process_token(token.m_token); + return m_impl.consume_token(token); } auto process_token(std::string const & token) const -> std::any { - auto const value = get_type_handler().from_string(token); - if (!value.has_value()) - { - throw parsing_error(std::format("argument {}: invalid value: '{}'", get_name_for_error(), token)); - } - check_choices(value); - return value; + return m_impl.process_token(token); } auto consume_tokens(std::ranges::view auto tokens) const -> std::vector { - auto result = std::vector(); - auto consumed = std::vector(); - for (auto & token : tokens) - { - result.push_back(process_token(token.m_token)); - consumed.push_back(&token); - } - std::ranges::for_each(consumed, [](auto token) { token->m_consumed = true; }); - return result; + return m_impl.consume_tokens(tokens); } auto check_choices(std::any const & value) const -> void { - if (get_choices().empty()) - { - return; - } - - if (!std::ranges::any_of( - get_choices(), - [&](auto const & rhs) { return get_type_handler().compare(value, rhs); })) - { - auto const message = std::format( - "argument {}: invalid choice: {} (choose from {})", - get_joined_names(), - get_type_handler().to_string(value), - get_joined_choices(", ")); - throw parsing_error(message); - } + m_impl.check_choices(value); } auto get_transformed(std::vector const & values) const -> std::any { - return get_type_handler().transform(values); + return m_impl.get_transformed(values); } auto get_size(std::any const & value) const -> std::size_t { - return get_type_handler().size(value); + return m_impl.get_size(value); } auto append_value(std::any const & value, std::any & values) const -> void { - get_type_handler().append(value, values); + m_impl.append_value(value, values); } + + virtual auto get_name_for_error() const -> std::string = 0; + + private: + ArgumentCommonImpl m_impl; }; - class PositionalArgument final : public ArgumentCommon + class PositionalArgument final : public ArgumentCommonBase { private: auto parse_arguments_option(std::ranges::view auto tokens) -> void @@ -926,7 +1036,7 @@ namespace argparse public: explicit PositionalArgument(Options options) - : ArgumentCommon(std::move(options)) + : ArgumentCommonBase(std::move(options)) { } @@ -999,7 +1109,7 @@ namespace argparse std::any m_value; }; - class OptionalArgument final : public ArgumentCommon + class OptionalArgument final : public ArgumentCommonBase { private: auto perform_action(std::string const & value, std::ranges::view auto tokens) -> void @@ -1294,7 +1404,7 @@ namespace argparse public: explicit OptionalArgument(Options options) - : ArgumentCommon(std::move(options)) + : ArgumentCommonBase(std::move(options)) { } @@ -1386,7 +1496,7 @@ namespace argparse } }; - using ArgumentUptr = std::unique_ptr; + using ArgumentUptr = std::unique_ptr; using ArgumentUptrs = std::vector; class Formatter