diff --git a/include/argparse.hpp b/include/argparse.hpp index 26bbf08..30ac958 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -916,7 +916,6 @@ namespace argparse return m_impl.get_nargs_option(); } - protected: auto get_default() const -> std::any const & { return m_impl.get_default(); @@ -1002,6 +1001,355 @@ namespace argparse ArgumentImpl m_impl; }; + class StoreAction + { + public: + StoreAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & value, std::ranges::view auto tokens) const -> void + { + if (m_base.has_nargs()) + { + if (m_base.has_nargs_number()) + { + parse_arguments_number(tokens); + } + else + { + parse_arguments_option(tokens); + } + } + else + { + if (value.empty()) + { + m_value = m_base.consume_token(tokens.front()); + } + else + { + m_value = m_base.process_token(value); + } + } + } + + auto check_errors(std::string const & value, std::ranges::view auto tokens) const -> void + { + if (!m_base.has_nargs() && value.empty() && tokens.empty()) + { + throw parsing_error(std::format("argument {}: expected one argument", m_base.get_joined_names())); + } + } + + auto assign_non_present_value() const -> void + { + m_value = m_base.get_default(); + } + + private: + auto parse_arguments_number(std::ranges::view auto tokens) const -> void + { + auto const nargs_number = m_base.get_nargs_number(); + auto const values = m_base.consume_tokens(tokens | std::views::take(nargs_number)); + if (values.size() < nargs_number) + { + throw parsing_error(std::format("argument {}: expected {} argument{}", m_base.get_joined_names(), std::to_string(nargs_number), nargs_number > 1 ? "s" : "")); + } + m_value = m_base.get_transformed(values); + } + + auto parse_arguments_option(std::ranges::view auto tokens) const -> void + { + switch (m_base.get_nargs_option()) + { + case zero_or_one: + { + if (!tokens.empty()) + { + m_value = m_base.consume_token(tokens.front()); + } + else + { + m_value = m_base.get_const(); + } + break; + } + case zero_or_more: + { + m_value = m_base.parse_arguments(tokens); + break; + } + case one_or_more: + { + if (auto const values = m_base.consume_tokens(tokens); !values.empty()) + { + m_value = m_base.get_transformed(values); + } + else + { + throw parsing_error(std::format("argument {}: expected at least one argument", m_base.get_joined_names())); + } + break; + } + } + } + + private: + ArgumentBase & m_base; + std::any & m_value; + }; + + class StoreConstAction + { + public: + StoreConstAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + m_value = m_base.get_const(); + } + + auto check_errors(std::string const & value, std::ranges::view auto /* tokens */) const -> void + { + if (!value.empty()) + { + throw parsing_error(std::format("argument {}: ignored explicit argument '{}'", m_base.get_joined_names(), value)); + } + } + + auto assign_non_present_value() const -> void + { + m_value = m_base.get_default(); + } + + private: + ArgumentBase & m_base; + std::any & m_value; + }; + + class StoreTrueAction + { + public: + StoreTrueAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + m_value = true; + } + + auto check_errors(std::string const & value, std::ranges::view auto /* tokens */) const -> void + { + if (!value.empty()) + { + throw parsing_error(std::format("argument {}: ignored explicit argument '{}'", m_base.get_joined_names(), value)); + } + } + + auto assign_non_present_value() const -> void + { + m_value = false; + } + + private: + ArgumentBase & m_base; + std::any & m_value; + }; + + class StoreFalseAction + { + public: + StoreFalseAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + m_value = false; + } + + auto check_errors(std::string const & value, std::ranges::view auto /* tokens */) const -> void + { + if (!value.empty()) + { + throw parsing_error(std::format("argument {}: ignored explicit argument '{}'", m_base.get_joined_names(), value)); + } + } + + auto assign_non_present_value() const -> void + { + m_value = true; + } + + private: + ArgumentBase & m_base; + std::any & m_value; + }; + + class HelpAction + { + public: + explicit HelpAction(std::any & value) + : m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + m_value = true; + throw HelpRequested(); + } + + auto check_errors(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + } + + auto assign_non_present_value() const -> void + { + m_value = false; + } + + private: + std::any & m_value; + }; + + class VersionAction + { + public: + explicit VersionAction(std::any & value) + : m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + m_value = true; + throw VersionRequested(); + } + + auto check_errors(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + } + + auto assign_non_present_value() const -> void + { + m_value = false; + } + + private: + std::any & m_value; + }; + + class CountAction + { + public: + CountAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & /* value */, std::ranges::view auto /* tokens */) const -> void + { + if (!m_value.has_value()) + { + m_value = 1; + } + else + { + ++std::any_cast(m_value); + } + } + + auto check_errors(std::string const & value, std::ranges::view auto /* tokens */) const -> void + { + if (!value.empty()) + { + throw parsing_error(std::format("argument {}: ignored explicit argument '{}'", m_base.get_joined_names(), value)); + } + } + + auto assign_non_present_value() const -> void + { + m_value = m_base.get_default(); + } + + private: + ArgumentBase & m_base; + std::any & m_value; + std::any m_default; + }; + + class AppendAction + { + public: + AppendAction(ArgumentBase & base, std::any & value) + : m_base(base) + , m_value(value) + { + } + + auto perform(std::string const & value, std::ranges::view auto tokens) const -> void + { + if (value.empty()) + { + if (!m_value.has_value()) + { + auto const values = m_base.consume_tokens(tokens | std::views::take(1)); + m_value = m_base.get_transformed(values); + } + else + { + auto const val = m_base.consume_token(tokens.front()); + m_base.append_value(val, m_value); + } + } + else + { + if (!m_value.has_value()) + { + auto const values = m_base.consume_tokens(std::views::single(Token{value})); + m_value = m_base.get_transformed(values); + } + else + { + auto const val = m_base.process_token(value); + m_base.append_value(val, m_value); + } + } + } + + auto check_errors(std::string const & value, std::ranges::view auto tokens) const -> void + { + if (value.empty() && tokens.empty()) + { + throw parsing_error(std::format("argument {}: expected one argument", m_base.get_joined_names())); + } + } + + auto assign_non_present_value() const -> void + { + m_value = m_base.get_default(); + } + + private: + ArgumentBase & m_base; + std::any & m_value; + std::any m_default; + }; + class PositionalArgument final : public ArgumentBase { private: @@ -1156,133 +1504,7 @@ namespace argparse private: auto perform_action(std::string const & value, std::ranges::view auto tokens) -> void { - switch (get_action()) - { - case store: - if (has_nargs()) - { - if (has_nargs_number()) - { - parse_arguments_number(tokens); - } - else - { - parse_arguments_option(tokens); - } - } - else - { - if (value.empty()) - { - m_value = consume_token(tokens.front()); - } - else - { - m_value = process_token(value); - } - } - break; - case store_true: - m_value = true; - break; - case store_false: - m_value = false; - break; - case store_const: - m_value = get_const(); - break; - case count: - if (!m_value.has_value()) - { - m_value = 1; - } - else - { - ++std::any_cast(m_value); - } - break; - case append: - if (value.empty()) - { - if (!m_value.has_value()) - { - auto const values = consume_tokens(tokens | std::views::take(1)); - m_value = get_transformed(values); - } - else - { - auto const val = consume_token(tokens.front()); - append_value(val, m_value); - } - } - else - { - if (!m_value.has_value()) - { - auto const values = consume_tokens(std::views::single(Token{value})); - m_value = get_transformed(values); - } - else - { - auto const val = process_token(value); - append_value(val, m_value); - } - } - break; - case help: - m_value = true; - throw HelpRequested(); - case version: - m_value = true; - throw VersionRequested(); - } - } - - auto parse_arguments_number(std::ranges::view auto tokens) -> void - { - auto const nargs_number = get_nargs_number(); - auto const values = consume_tokens(tokens | std::views::take(nargs_number)); - if (values.size() < nargs_number) - { - throw parsing_error(std::format("argument {}: expected {} argument{}", get_joined_names(), std::to_string(nargs_number), nargs_number > 1 ? "s" : "")); - } - m_value = get_transformed(values); - } - - auto parse_arguments_option(std::ranges::view auto tokens) -> void - { - switch (get_nargs_option()) - { - case zero_or_one: - { - if (!tokens.empty()) - { - m_value = consume_token(tokens.front()); - } - else - { - m_value = get_const(); - } - break; - } - case zero_or_more: - { - m_value = parse_arguments(tokens); - break; - } - case one_or_more: - { - if (auto const values = consume_tokens(tokens); !values.empty()) - { - m_value = get_transformed(values); - } - else - { - throw parsing_error(std::format("argument {}: expected at least one argument", get_joined_names())); - } - break; - } - } + std::visit([&](auto const & action) { action.perform(value, tokens); }, m_action); } auto has_arg(auto it) const -> std::string_view @@ -1379,54 +1601,12 @@ namespace argparse auto check_errors(std::string const & value, std::ranges::view auto tokens) const -> void { - switch (get_action()) - { - case store: - if (!has_nargs() && value.empty() && tokens.empty()) - { - throw parsing_error(std::format("argument {}: expected one argument", get_joined_names())); - } - break; - case store_true: - case store_false: - case store_const: - case count: - if (!value.empty()) - { - throw parsing_error(std::format("argument {}: ignored explicit argument '{}'", get_joined_names(), value)); - } - break; - case append: - if (value.empty() && tokens.empty()) - { - throw parsing_error(std::format("argument {}: expected one argument", get_joined_names())); - } - break; - case help: - case version: - break; - } + std::visit([&](auto const & action) { action.check_errors(value, tokens); }, m_action); } auto assign_non_present_value() -> void { - switch (get_action()) - { - case store_true: - case help: - case version: - m_value = false; - break; - case store_false: - m_value = true; - break; - case store: - case store_const: - case count: - case append: - m_value = get_default(); - break; - } + std::visit([&](auto const & action) { action.assign_non_present_value(); }, m_action); } static auto get_consumable(Tokens & tokens) @@ -1453,13 +1633,40 @@ namespace argparse }); } + auto create_action() -> std::variant + { + switch (get_action()) + { + case store: + return StoreAction(*this, m_value); + case store_true: + return StoreTrueAction(*this, m_value); + case store_false: + return StoreFalseAction(*this, m_value); + case store_const: + return StoreConstAction(*this, m_value); + case help: + return HelpAction(m_value); + case version: + return VersionAction(m_value); + case count: + return CountAction(*this, m_value); + case append: + return AppendAction(*this, m_value); + default: + return StoreAction(*this, m_value); + } + } + private: std::any m_value; bool m_present = false; + std::variant m_action; public: explicit OptionalArgument(Options options) : ArgumentBase(std::move(options)) + , m_action(create_action()) { }