From 21ca1f638be360ee4aecf8101fdcfe247e5cc3bf Mon Sep 17 00:00:00 2001 From: "killua.z" Date: Thu, 7 May 2026 17:49:47 +0400 Subject: [PATCH] chore: format --- examples/test/main.cpp | 145 ++- src/headers/argparse | 4 +- src/headers/conventions/base_convention.hpp | 6 +- .../gnu_argument_convention.hpp | 6 +- .../windows_argument_convention.hpp | 6 +- src/headers/parser/argument_builder.hpp | 999 +++++++++--------- src/headers/parser/argument_parser.hpp | 2 +- src/headers/parser/fake_parser.hpp | 2 +- src/headers/parser/parser_v2.hpp | 45 +- src/headers/parser/parsing_traits/traits.hpp | 52 +- src/main.cpp | 254 ----- src/source/conventions/base_convention.cpp | 2 +- .../gnu_argument_convention.cpp | 2 +- .../windows_argument_convention.cpp | 10 +- src/source/parser/argument_parser.cpp | 2 +- src/source/parser/fake_parser.cpp | 39 +- src/source/parser/parsing_traits/traits.cpp | 2 +- .../parser/platform_parsers/macos_parser.cpp | 26 +- src/test.cpp | 420 -------- 19 files changed, 667 insertions(+), 1357 deletions(-) delete mode 100644 src/main.cpp delete mode 100644 src/test.cpp diff --git a/examples/test/main.cpp b/examples/test/main.cpp index 1cbe83e..9d5feef 100644 --- a/examples/test/main.cpp +++ b/examples/test/main.cpp @@ -1,8 +1,8 @@ #include #include -#include #include +#include #include #include #include @@ -11,107 +11,94 @@ using argument = argument_parser::builder::argument<>; using argument_parser::parsing_traits::hint_type; -auto echo(std::string const& s) -> void { - std::cout << s << '\n'; +auto echo(std::string const &s) -> void { + std::cout << s << '\n'; } using namespace argument_parser::parsing_traits; constexpr hint_type vector_purpose_hint = "vector of "; -template -class parser_trait> { - public: - static std::vector parse(std::string const& s) { - std::vector result; - std::stringstream ss(s); +template class parser_trait> { +public: + static std::vector parse(std::string const &s) { + std::vector result; + std::stringstream ss(s); - for (std::string line; std::getline(ss, line, ',');) { - T item = parser_trait::parse(line); - result.push_back(item); - } - return result; - } + for (std::string line; std::getline(ss, line, ',');) { + T item = parser_trait::parse(line); + result.push_back(item); + } + return result; + } - ARGPARSE_TRAIT_FORMAT_HINT = concat< - hint_provider<&parser_trait::format_hint>, - hint_provider<&comma>, - hint_provider<&parser_trait::format_hint> - >; + ARGPARSE_TRAIT_FORMAT_HINT = concat::format_hint>, hint_provider<&comma>, + hint_provider<&parser_trait::format_hint>>; - ARGPARSE_TRAIT_PURPOSE_HINT = concat< - hint_provider<&vector_purpose_hint>, - hint_provider<&parser_trait::purpose_hint> - >; + ARGPARSE_TRAIT_PURPOSE_HINT = + concat, hint_provider<&parser_trait::purpose_hint>>; }; using namespace argument_parser::v2::flags; auto main() -> int { - argument_parser::v2::parser parser(false); + argument_parser::v2::parser parser(false); - argument::start() - .positional("count") - .position(1) - .help_text("How many times to repeat the action.") - .action([](int const& count) { - std::cout << "count action configured for " << count << '\n'; - }) - .build(parser); + argument::start() + .positional("count") + .position(1) + .help_text("How many times to repeat the action.") + .action([](int const &count) { std::cout << "count action configured for " << count << '\n'; }) + .build(parser); - int captured_value = 0; - argument::start() - .long_argument("threshold") - .help_text("Store the parsed value through a reference.") - .reference(captured_value) - .build(parser); + int captured_value = 0; + argument::start() + .long_argument("threshold") + .help_text("Store the parsed value through a reference.") + .reference(captured_value) + .build(parser); - argument::start() - .positional("captured") - .position(0) - .help_text("Store the parsed value through a reference.") - .reference(captured_value) - .build(parser); + argument::start() + .positional("captured") + .position(0) + .help_text("Store the parsed value through a reference.") + .reference(captured_value) + .build(parser); - // parser.add_argument({ - // {ShortArgument, "c"}, - // {HelpText, "capture count"}, - // {Reference, &captured_value}, - // }); + // parser.add_argument({ + // {ShortArgument, "c"}, + // {HelpText, "capture count"}, + // {Reference, &captured_value}, + // }); - argument::start() - .short_argument("q") - .help_text("Store a boolean flag.") - .build(parser); + argument::start().short_argument("q").help_text("Store a boolean flag.").build(parser); - // argument::start() - // .long_argument("regex") - // .help_text("Store a regex value.") - // .store>() - // .build(parser); + // argument::start() + // .long_argument("regex") + // .help_text("Store a regex value.") + // .store>() + // .build(parser); - argument::start() - .short_argument("e") - .long_argument("echo") - .help_text("Echo the parsed value.") - .action(echo) - .build(parser); + argument::start() + .short_argument("e") + .long_argument("echo") + .help_text("Echo the parsed value.") + .action(echo) + .build(parser); - argument::start() - .long_argument("vecstr") - .short_argument("vs") - .action>([](std::vector const& vecstr) { - for (auto const& str : vecstr) { - std::cout << str << '\n'; - } - }) - .build(parser); + argument::start() + .long_argument("vecstr") + .short_argument("vs") + .action>([](std::vector const &vecstr) { + for (auto const &str : vecstr) { + std::cout << str << '\n'; + } + }) + .build(parser); - parser.handle_arguments({ - &argument_parser::conventions::gnu_argument_convention - }); + parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention}); - std::cout << "captured value: " << captured_value << '\n'; + std::cout << "captured value: " << captured_value << '\n'; - return 0; + return 0; } diff --git a/src/headers/argparse b/src/headers/argparse index ff8e28f..7700b19 100644 --- a/src/headers/argparse +++ b/src/headers/argparse @@ -1,10 +1,10 @@ #pragma once #ifndef ARGPARSE_HPP #define ARGPARSE_HPP +#include "macros.h" +#include #include #include -#include -#include "macros.h" #ifdef __linux__ #include diff --git a/src/headers/conventions/base_convention.hpp b/src/headers/conventions/base_convention.hpp index 3c9463c..784a15f 100644 --- a/src/headers/conventions/base_convention.hpp +++ b/src/headers/conventions/base_convention.hpp @@ -20,8 +20,8 @@ namespace argument_parser::conventions { virtual std::string name() const = 0; virtual std::string short_prec() const = 0; virtual std::string long_prec() const = 0; - virtual std::pair make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const = 0; + virtual std::pair + make_help_text(std::string const &short_arg, std::string const &long_arg, bool requires_value) const = 0; virtual std::vector get_features() const = 0; protected: @@ -36,4 +36,4 @@ namespace argument_parser::conventions::helpers { std::string to_upper(std::string s); } // namespace argument_parser::conventions::helpers -#endif \ No newline at end of file +#endif diff --git a/src/headers/conventions/implementations/gnu_argument_convention.hpp b/src/headers/conventions/implementations/gnu_argument_convention.hpp index b79c689..c2082ef 100644 --- a/src/headers/conventions/implementations/gnu_argument_convention.hpp +++ b/src/headers/conventions/implementations/gnu_argument_convention.hpp @@ -15,7 +15,7 @@ namespace argument_parser::conventions::implementations { std::string short_prec() const override; std::string long_prec() const override; std::pair make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const override; + bool requires_value) const override; std::vector get_features() const override; static gnu_argument_convention instance; @@ -33,7 +33,7 @@ namespace argument_parser::conventions::implementations { std::string short_prec() const override; std::string long_prec() const override; std::pair make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const override; + bool requires_value) const override; std::vector get_features() const override; static gnu_equal_argument_convention instance; @@ -53,4 +53,4 @@ namespace argument_parser::conventions { implementations::gnu_equal_argument_convention::instance; } // namespace argument_parser::conventions -#endif \ No newline at end of file +#endif diff --git a/src/headers/conventions/implementations/windows_argument_convention.hpp b/src/headers/conventions/implementations/windows_argument_convention.hpp index 5a6de0b..80f54fa 100644 --- a/src/headers/conventions/implementations/windows_argument_convention.hpp +++ b/src/headers/conventions/implementations/windows_argument_convention.hpp @@ -19,7 +19,7 @@ namespace argument_parser::conventions::implementations { std::string short_prec() const override; std::string long_prec() const override; std::pair make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const override; + bool requires_value) const override; std::vector get_features() const override; static windows_argument_convention instance; @@ -38,7 +38,7 @@ namespace argument_parser::conventions::implementations { std::string short_prec() const override; std::string long_prec() const override; std::pair make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const override; + bool requires_value) const override; std::vector get_features() const override; static windows_kv_argument_convention instance; @@ -60,4 +60,4 @@ namespace argument_parser::conventions { implementations::windows_kv_argument_convention::instance; } // namespace argument_parser::conventions -#endif // WINDOWS_ARGUMENT_CONVENTION_HPP \ No newline at end of file +#endif // WINDOWS_ARGUMENT_CONVENTION_HPP diff --git a/src/headers/parser/argument_builder.hpp b/src/headers/parser/argument_builder.hpp index 26aa89b..d372f03 100644 --- a/src/headers/parser/argument_builder.hpp +++ b/src/headers/parser/argument_builder.hpp @@ -2,516 +2,509 @@ #include "argument_parser.hpp" #include -#include #include +#include #ifndef ARGUMENT_PARSER_PARSER_V3_HPP #define ARGUMENT_PARSER_PARSER_V3_HPP namespace argument_parser::builder { -class non_type {}; -namespace builder_mask { - using v2_flag = argument_parser::v2::add_argument_flags; - using mask_type = std::uint64_t; - enum class value_mode { - unresolved, - store, - flag, - reference, - nonparametered_action, - parametered_action - }; - - enum class extra_capability : unsigned { - Store = static_cast(v2_flag::Reference) + 1, - Flag - }; - - constexpr auto bit(v2_flag flag) -> mask_type { - return mask_type{1} << static_cast(flag); - } - - constexpr auto bit(extra_capability capability) -> mask_type { - return mask_type{1} << static_cast(capability); - } - - constexpr mask_type short_argument = bit(v2_flag::ShortArgument); - constexpr mask_type long_argument = bit(v2_flag::LongArgument); - constexpr mask_type positional = bit(v2_flag::Positional); - constexpr mask_type position = bit(v2_flag::Position); - constexpr mask_type help_text = bit(v2_flag::HelpText); - constexpr mask_type action = bit(v2_flag::Action); - constexpr mask_type required = bit(v2_flag::Required); - constexpr mask_type reference = bit(v2_flag::Reference); - constexpr mask_type store = bit(extra_capability::Store); - constexpr mask_type flag = bit(extra_capability::Flag); - - constexpr mask_type value_mode_group = action | reference | store | flag; - constexpr mask_type initial = short_argument | long_argument | positional | help_text | action | required | - reference | store | flag; - - constexpr auto has(mask_type mask, mask_type capability) -> bool { - return (mask & capability) == capability; - } - - constexpr auto remove(mask_type mask, mask_type capability) -> mask_type { - return mask & ~capability; - } - - constexpr auto replace(mask_type mask, mask_type remove_bits, mask_type add_bits = 0) -> mask_type { - return (mask & ~remove_bits) | add_bits; - } - - constexpr auto has_selected_identifier(mask_type mask) -> bool { - return !has(mask, short_argument) || !has(mask, long_argument) || !has(mask, positional); - } - - constexpr auto is_buildable(mask_type mask) -> bool { - return has_selected_identifier(mask); - } -} // namespace builder_mask - -template -class argument { -public: - using mask_type = builder_mask::mask_type; - using v2_flag = argument_parser::v2::add_argument_flags; - using value_mode = builder_mask::value_mode; - - static auto start() -> argument { - return {}; - } - - template = 0> - auto short_argument(std::string short_name) const - -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_short_argument = std::move(short_name); - return next; - } - - template = 0> - auto long_argument(std::string long_name) const - -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_long_argument = std::move(long_name); - return next; - } - - template = 0> - auto positional(std::string positional_name) const - -> argument { - using next_argument = - argument; - - next_argument next{*this}; - next.m_positional_name = std::move(positional_name); - return next; - } - - template = 0> - auto position(int index) const -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_position = index; - return next; - } - - template = 0> - auto help_text(std::string help) const -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_help_text = std::move(help); - return next; - } - - template = 0> - auto required(bool value = true) const -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_required = value; - return next; - } - - template = 0> - auto store() const -> argument { - static_assert(!std::is_same_v, "store() is not supported. Use flag() for boolean-style arguments."); - - using next_argument = argument; - next_argument next{*this}; - next.m_value_mode = value_mode::store; - return next; - } - - template = 0> - auto flag() const -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_value_mode = value_mode::flag; - return next; - } - - template = 0, - typename T> - auto reference(T& value) const -> argument { - using next_argument = argument; - - next_argument next{*this}; - next.m_reference = std::addressof(value); - next.m_value_mode = value_mode::reference; - return next; - } - - template = 0, - typename Callable> - auto action(Callable&& handler) const - -> std::enable_if_t, - argument> { - using next_argument = - argument; - - next_argument next{*this}; - next.m_action = std::make_shared( - std::function(std::forward(handler))); - next.m_value_mode = value_mode::nonparametered_action; - return next; - } - - template = 0, typename Callable> - auto action(Callable&& handler) const - -> std::enable_if_t, - argument> { - static_assert(!std::is_same_v, "action(...) is not supported. Use action([] { ... }) instead."); - - using next_argument = argument; - - next_argument next{*this}; - next.m_action = std::make_shared>( - std::function(std::forward(handler))); - next.m_value_mode = value_mode::parametered_action; - return next; - } - - template = 0> - auto build(argument_parser::v2::base_parser& parser) const -> void { - assert_has_identifier(); - - switch (m_value_mode) { - case value_mode::flag: - build_flag(parser); - return; - case value_mode::nonparametered_action: - build_nonparametered_action(parser); - return; - case value_mode::store: - if constexpr (!std::is_same_v) { - build_store(parser); - return; - } - break; - case value_mode::reference: - if constexpr (!std::is_same_v) { - build_reference(parser); - return; - } - break; - case value_mode::parametered_action: - if constexpr (!std::is_same_v) { - build_parametered_action(parser); - return; - } - break; - case value_mode::unresolved: - if (is_positional()) { - build_default_positional(parser); - } else { - build_flag(parser); - } - return; - } - - throw std::logic_error("The builder reached build() without a supported terminal value mode."); - } - -private: - argument() = default; - - template - argument(argument const& other) - : m_short_argument(other.m_short_argument), - m_long_argument(other.m_long_argument), - m_positional_name(other.m_positional_name), - m_position(other.m_position), - m_help_text(other.m_help_text), - m_required(other.m_required), - m_action(other.m_action), - m_reference(copy_reference(other.m_reference)), - m_value_mode(other.m_value_mode) {} - - template - using typed_map = std::unordered_map>; - - using non_typed_map = std::unordered_map; - - auto is_positional() const -> bool { - return !m_positional_name.empty(); - } - - auto lookup_key() const -> std::string { - if (is_positional()) { - return m_positional_name; - } - if (!m_long_argument.empty()) { - return m_long_argument; - } - if (!m_short_argument.empty()) { - return m_short_argument; - } - - throw std::logic_error("No argument identifier is available for lookup."); - } - - auto assert_has_identifier() const -> void { - if (!is_positional() && m_short_argument.empty() && m_long_argument.empty()) { - throw std::logic_error("build() requires short_argument(), long_argument(), or positional()."); - } - } - - template - auto add_common_pairs(Map& pairs) const -> void { - using namespace argument_parser::v2::flags; - - if (is_positional()) { - pairs[Positional] = m_positional_name; - if (m_position.has_value()) { - pairs[Position] = m_position.value(); - } - } else { - if (!m_short_argument.empty()) { - pairs[ShortArgument] = m_short_argument; - } - if (!m_long_argument.empty()) { - pairs[LongArgument] = m_long_argument; - } - } - - if (!m_help_text.empty()) { - pairs[HelpText] = m_help_text; - } - if (m_required) { - pairs[Required] = true; - } - } - - template - auto make_typed_pairs() const -> typed_map { - typed_map pairs; - add_common_pairs(pairs); - return pairs; - } - - auto make_non_typed_pairs() const -> non_typed_map { - non_typed_map pairs; - add_common_pairs(pairs); - return pairs; - } - - auto build_flag(argument_parser::v2::base_parser& parser) const -> void { - auto pairs = make_non_typed_pairs(); - parser.add_argument(pairs); - } - - auto build_default_positional(argument_parser::v2::base_parser& parser) const -> void { - auto pairs = make_non_typed_pairs(); - parser.add_argument(pairs); - } - - auto build_store(argument_parser::v2::base_parser& parser) const -> void { - auto pairs = make_typed_pairs(); - parser.template add_argument(pairs); - } - - auto build_reference(argument_parser::v2::base_parser& parser) const -> void { - auto pairs = make_typed_pairs(); - auto* target = m_reference; - auto key = lookup_key(); - - if (target == nullptr) { - throw std::logic_error("reference() was selected without a target."); - } - - pairs[argument_parser::v2::flags::Reference] = target; - parser.template add_argument(pairs); - } - - auto build_parametered_action(argument_parser::v2::base_parser& parser) const -> void { - auto const* typed_action = dynamic_cast const*>(m_action.get()); - if (typed_action == nullptr) { - throw std::logic_error("Stored action is not compatible with the requested parameter type."); - } - - auto pairs = make_typed_pairs(); - pairs[argument_parser::v2::flags::Action] = *typed_action; - parser.template add_argument(pairs); - } - - auto build_nonparametered_action(argument_parser::v2::base_parser& parser) const -> void { - auto const* nonparametered_action = dynamic_cast(m_action.get()); - if (nonparametered_action == nullptr) { - throw std::logic_error("Stored action is not a non-parametered action."); - } - - if (is_positional()) { - auto wrapped_action = argument_parser::helpers::make_parametered_action( - [action = *nonparametered_action](std::string const&) { action.invoke(); }); - auto pairs = make_typed_pairs(); - pairs[argument_parser::v2::flags::Action] = wrapped_action; - parser.template add_argument(pairs); - return; - } - - auto pairs = make_non_typed_pairs(); - pairs[argument_parser::v2::flags::Action] = *nonparametered_action; - parser.add_argument(pairs); - } - - std::string m_short_argument{}; - std::string m_long_argument{}; - std::string m_positional_name{}; - std::optional m_position{}; - std::string m_help_text{}; - bool m_required = false; - std::shared_ptr m_action{}; - store_type* m_reference = nullptr; - value_mode m_value_mode = value_mode::unresolved; - - template - static auto copy_reference(other_store_type* reference) -> store_type* { - if constexpr (std::is_same_v) { - return reference; - } else { - return nullptr; - } - } - - template - friend class argument; -}; - -namespace assertions { - struct noop_handler { - void operator()() const {} - }; - - template - struct parameter_sink { - void operator()(T const&) const {} - }; - - template - struct can_use_help_text : std::false_type {}; - - template - struct can_use_help_text().help_text(std::declval()))>> : std::true_type {}; - - template - struct can_use_position : std::false_type {}; - - template - struct can_use_position().position(0))>> : std::true_type {}; - - template - struct can_use_nonparametered_action : std::false_type {}; - - template - struct can_use_nonparametered_action().action(noop_handler{}))>> : std::true_type {}; - - template - struct can_use_parametered_action : std::false_type {}; - - template - struct can_use_parametered_action().template action(parameter_sink{}))>> - : std::true_type {}; - - template - struct can_use_store : std::false_type {}; - - template - struct can_use_store().template store())>> : std::true_type {}; - - template - struct can_use_flag : std::false_type {}; - - template - struct can_use_flag().flag())>> : std::true_type {}; - - template - struct can_use_reference : std::false_type {}; - - template - struct can_use_reference().reference(std::declval()))>> : std::true_type {}; - - using after_help_text = decltype(argument<>::start().help_text("help")); - static_assert(!can_use_help_text::value, "help_text() should be single-use."); - - using after_positional = decltype(argument<>::start().positional("path")); - static_assert(can_use_position::value, "positional() should unlock position()."); - - using after_position = decltype(argument<>::start().positional("path").position(0)); - static_assert(!can_use_position::value, "position() should be single-use."); - - using after_positional_mode_selection = decltype(argument<>::start().positional("path").store<>()); - static_assert(!can_use_flag::value, "flag() should not be available for positional arguments."); - - using after_nonparametered_action = - decltype(argument<>::start().short_argument("v").help_text("verbose").action(noop_handler{})); - static_assert(!can_use_nonparametered_action::value, - "action() should not remain callable after selecting a non-parametered action."); - static_assert(!can_use_parametered_action::value, - "typed action() should also be disabled after selecting a non-parametered action."); - static_assert(!can_use_store::value, - "store() should be mutually exclusive with action()."); - static_assert(!can_use_flag::value, - "flag() should be mutually exclusive with action()."); - static_assert(!can_use_reference::value, - "reference() should be mutually exclusive with action()."); - - using after_parametered_action = - decltype(argument<>::start().positional("count").position(0).action(parameter_sink{})); - static_assert(!can_use_nonparametered_action::value, - "non-parametered action() should be disabled after selecting a typed action."); - static_assert(!can_use_parametered_action::value, - "typed action() should be single-use regardless of the parameter type."); + class non_type {}; + namespace builder_mask { + using v2_flag = argument_parser::v2::add_argument_flags; + using mask_type = std::uint64_t; + enum class value_mode { unresolved, store, flag, reference, nonparametered_action, parametered_action }; + + enum class extra_capability : unsigned { Store = static_cast(v2_flag::Reference) + 1, Flag }; + + constexpr auto bit(v2_flag flag) -> mask_type { + return mask_type{1} << static_cast(flag); + } + + constexpr auto bit(extra_capability capability) -> mask_type { + return mask_type{1} << static_cast(capability); + } + + constexpr mask_type short_argument = bit(v2_flag::ShortArgument); + constexpr mask_type long_argument = bit(v2_flag::LongArgument); + constexpr mask_type positional = bit(v2_flag::Positional); + constexpr mask_type position = bit(v2_flag::Position); + constexpr mask_type help_text = bit(v2_flag::HelpText); + constexpr mask_type action = bit(v2_flag::Action); + constexpr mask_type required = bit(v2_flag::Required); + constexpr mask_type reference = bit(v2_flag::Reference); + constexpr mask_type store = bit(extra_capability::Store); + constexpr mask_type flag = bit(extra_capability::Flag); + + constexpr mask_type value_mode_group = action | reference | store | flag; + constexpr mask_type initial = + short_argument | long_argument | positional | help_text | action | required | reference | store | flag; + + constexpr auto has(mask_type mask, mask_type capability) -> bool { + return (mask & capability) == capability; + } + + constexpr auto remove(mask_type mask, mask_type capability) -> mask_type { + return mask & ~capability; + } + + constexpr auto replace(mask_type mask, mask_type remove_bits, mask_type add_bits = 0) -> mask_type { + return (mask & ~remove_bits) | add_bits; + } + + constexpr auto has_selected_identifier(mask_type mask) -> bool { + return !has(mask, short_argument) || !has(mask, long_argument) || !has(mask, positional); + } + + constexpr auto is_buildable(mask_type mask) -> bool { + return has_selected_identifier(mask); + } + } // namespace builder_mask + + template class argument { + public: + using mask_type = builder_mask::mask_type; + using v2_flag = argument_parser::v2::add_argument_flags; + using value_mode = builder_mask::value_mode; + + static auto start() -> argument { + return {}; + } + + template = 0> + auto short_argument(std::string short_name) const + -> argument { + using next_argument = + argument; + + next_argument next{*this}; + next.m_short_argument = std::move(short_name); + return next; + } + + template = 0> + auto long_argument(std::string long_name) const + -> argument { + using next_argument = + argument; + + next_argument next{*this}; + next.m_long_argument = std::move(long_name); + return next; + } + + template = 0> + auto positional(std::string positional_name) const + -> argument { + using next_argument = + argument; + + next_argument next{*this}; + next.m_positional_name = std::move(positional_name); + return next; + } + + template = 0> + auto position(int index) const + -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_position = index; + return next; + } + + template = 0> + auto help_text(std::string help) const + -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_help_text = std::move(help); + return next; + } + + template = 0> + auto required(bool value = true) const + -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_required = value; + return next; + } + + template = 0> + auto store() const -> argument { + static_assert(!std::is_same_v, + "store() is not supported. Use flag() for boolean-style arguments."); + + using next_argument = argument; + next_argument next{*this}; + next.m_value_mode = value_mode::store; + return next; + } + + template = 0> + auto flag() const -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_value_mode = value_mode::flag; + return next; + } + + template = 0, typename T> + auto reference(T &value) const + -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_reference = std::addressof(value); + next.m_value_mode = value_mode::reference; + return next; + } + + template = 0, typename Callable> + auto action(Callable &&handler) const -> std::enable_if_t< + std::is_invocable_r_v, + argument> { + using next_argument = + argument; + + next_argument next{*this}; + next.m_action = std::make_shared( + std::function(std::forward(handler))); + next.m_value_mode = value_mode::nonparametered_action; + return next; + } + + template = 0, typename Callable> + auto action(Callable &&handler) const + -> std::enable_if_t, + argument> { + static_assert(!std::is_same_v, + "action(...) is not supported. Use action([] { ... }) instead."); + + using next_argument = argument; + + next_argument next{*this}; + next.m_action = std::make_shared>( + std::function(std::forward(handler))); + next.m_value_mode = value_mode::parametered_action; + return next; + } + + template = 0> + auto build(argument_parser::v2::base_parser &parser) const -> void { + assert_has_identifier(); + + switch (m_value_mode) { + case value_mode::flag: + build_flag(parser); + return; + case value_mode::nonparametered_action: + build_nonparametered_action(parser); + return; + case value_mode::store: + if constexpr (!std::is_same_v) { + build_store(parser); + return; + } + break; + case value_mode::reference: + if constexpr (!std::is_same_v) { + build_reference(parser); + return; + } + break; + case value_mode::parametered_action: + if constexpr (!std::is_same_v) { + build_parametered_action(parser); + return; + } + break; + case value_mode::unresolved: + if (is_positional()) { + build_default_positional(parser); + } else { + build_flag(parser); + } + return; + } + + throw std::logic_error("The builder reached build() without a supported terminal value mode."); + } + + private: + argument() = default; + + template + argument(argument const &other) + : m_short_argument(other.m_short_argument), m_long_argument(other.m_long_argument), + m_positional_name(other.m_positional_name), m_position(other.m_position), m_help_text(other.m_help_text), + m_required(other.m_required), m_action(other.m_action), m_reference(copy_reference(other.m_reference)), + m_value_mode(other.m_value_mode) {} + + template + using typed_map = + std::unordered_map>; + + using non_typed_map = std::unordered_map; + + auto is_positional() const -> bool { + return !m_positional_name.empty(); + } + + auto lookup_key() const -> std::string { + if (is_positional()) { + return m_positional_name; + } + if (!m_long_argument.empty()) { + return m_long_argument; + } + if (!m_short_argument.empty()) { + return m_short_argument; + } + + throw std::logic_error("No argument identifier is available for lookup."); + } + + auto assert_has_identifier() const -> void { + if (!is_positional() && m_short_argument.empty() && m_long_argument.empty()) { + throw std::logic_error("build() requires short_argument(), long_argument(), or positional()."); + } + } + + template auto add_common_pairs(Map &pairs) const -> void { + using namespace argument_parser::v2::flags; + + if (is_positional()) { + pairs[Positional] = m_positional_name; + if (m_position.has_value()) { + pairs[Position] = m_position.value(); + } + } else { + if (!m_short_argument.empty()) { + pairs[ShortArgument] = m_short_argument; + } + if (!m_long_argument.empty()) { + pairs[LongArgument] = m_long_argument; + } + } + + if (!m_help_text.empty()) { + pairs[HelpText] = m_help_text; + } + if (m_required) { + pairs[Required] = true; + } + } + + template auto make_typed_pairs() const -> typed_map { + typed_map pairs; + add_common_pairs(pairs); + return pairs; + } + + auto make_non_typed_pairs() const -> non_typed_map { + non_typed_map pairs; + add_common_pairs(pairs); + return pairs; + } + + auto build_flag(argument_parser::v2::base_parser &parser) const -> void { + auto pairs = make_non_typed_pairs(); + parser.add_argument(pairs); + } + + auto build_default_positional(argument_parser::v2::base_parser &parser) const -> void { + auto pairs = make_non_typed_pairs(); + parser.add_argument(pairs); + } + + auto build_store(argument_parser::v2::base_parser &parser) const -> void { + auto pairs = make_typed_pairs(); + parser.template add_argument(pairs); + } + + auto build_reference(argument_parser::v2::base_parser &parser) const -> void { + auto pairs = make_typed_pairs(); + auto *target = m_reference; + auto key = lookup_key(); + + if (target == nullptr) { + throw std::logic_error("reference() was selected without a target."); + } + + pairs[argument_parser::v2::flags::Reference] = target; + parser.template add_argument(pairs); + } + + auto build_parametered_action(argument_parser::v2::base_parser &parser) const -> void { + auto const *typed_action = + dynamic_cast const *>(m_action.get()); + if (typed_action == nullptr) { + throw std::logic_error("Stored action is not compatible with the requested parameter type."); + } + + auto pairs = make_typed_pairs(); + pairs[argument_parser::v2::flags::Action] = *typed_action; + parser.template add_argument(pairs); + } + + auto build_nonparametered_action(argument_parser::v2::base_parser &parser) const -> void { + auto const *nonparametered_action = + dynamic_cast(m_action.get()); + if (nonparametered_action == nullptr) { + throw std::logic_error("Stored action is not a non-parametered action."); + } + + if (is_positional()) { + auto wrapped_action = argument_parser::helpers::make_parametered_action( + [action = *nonparametered_action](std::string const &) { action.invoke(); }); + auto pairs = make_typed_pairs(); + pairs[argument_parser::v2::flags::Action] = wrapped_action; + parser.template add_argument(pairs); + return; + } + + auto pairs = make_non_typed_pairs(); + pairs[argument_parser::v2::flags::Action] = *nonparametered_action; + parser.add_argument(pairs); + } + + std::string m_short_argument{}; + std::string m_long_argument{}; + std::string m_positional_name{}; + std::optional m_position{}; + std::string m_help_text{}; + bool m_required = false; + std::shared_ptr m_action{}; + store_type *m_reference = nullptr; + value_mode m_value_mode = value_mode::unresolved; + + template static auto copy_reference(other_store_type *reference) -> store_type * { + if constexpr (std::is_same_v) { + return reference; + } else { + return nullptr; + } + } + + template friend class argument; + }; + + namespace assertions { + struct noop_handler { + void operator()() const {} + }; + + template struct parameter_sink { + void operator()(T const &) const {} + }; + + template struct can_use_help_text : std::false_type {}; + + template + struct can_use_help_text().help_text(std::declval()))>> + : std::true_type {}; + + template struct can_use_position : std::false_type {}; + + template + struct can_use_position().position(0))>> : std::true_type {}; + + template struct can_use_nonparametered_action : std::false_type {}; + + template + struct can_use_nonparametered_action().action(noop_handler{}))>> + : std::true_type {}; + + template struct can_use_parametered_action : std::false_type {}; + + template + struct can_use_parametered_action< + T, U, std::void_t().template action(parameter_sink{}))>> : std::true_type {}; + + template struct can_use_store : std::false_type {}; + + template + struct can_use_store().template store())>> : std::true_type {}; + + template struct can_use_flag : std::false_type {}; + + template + struct can_use_flag().flag())>> : std::true_type {}; + + template struct can_use_reference : std::false_type {}; + + template + struct can_use_reference().reference(std::declval()))>> + : std::true_type {}; + + using after_help_text = decltype(argument<>::start().help_text("help")); + static_assert(!can_use_help_text::value, "help_text() should be single-use."); + + using after_positional = decltype(argument<>::start().positional("path")); + static_assert(can_use_position::value, "positional() should unlock position()."); + + using after_position = decltype(argument<>::start().positional("path").position(0)); + static_assert(!can_use_position::value, "position() should be single-use."); + + using after_positional_mode_selection = decltype(argument<>::start().positional("path").store<>()); + static_assert(!can_use_flag::value, + "flag() should not be available for positional arguments."); + + using after_nonparametered_action = + decltype(argument<>::start().short_argument("v").help_text("verbose").action(noop_handler{})); + static_assert(!can_use_nonparametered_action::value, + "action() should not remain callable after selecting a non-parametered action."); + static_assert(!can_use_parametered_action::value, + "typed action() should also be disabled after selecting a non-parametered action."); + static_assert(!can_use_store::value, + "store() should be mutually exclusive with action()."); + static_assert(!can_use_flag::value, + "flag() should be mutually exclusive with action()."); + static_assert(!can_use_reference::value, + "reference() should be mutually exclusive with action()."); -} -} + using after_parametered_action = + decltype(argument<>::start().positional("count").position(0).action(parameter_sink{})); + static_assert(!can_use_nonparametered_action::value, + "non-parametered action() should be disabled after selecting a typed action."); + static_assert(!can_use_parametered_action::value, + "typed action() should be single-use regardless of the parameter type."); + + } // namespace assertions +} // namespace argument_parser::builder #endif diff --git a/src/headers/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp index 0776e46..53a1d68 100644 --- a/src/headers/parser/argument_parser.hpp +++ b/src/headers/parser/argument_parser.hpp @@ -425,4 +425,4 @@ namespace argument_parser { }; } // namespace argument_parser -#endif // ARGUMENT_PARSER_HPP \ No newline at end of file +#endif // ARGUMENT_PARSER_HPP diff --git a/src/headers/parser/fake_parser.hpp b/src/headers/parser/fake_parser.hpp index b95d3d4..47ac13b 100644 --- a/src/headers/parser/fake_parser.hpp +++ b/src/headers/parser/fake_parser.hpp @@ -31,7 +31,7 @@ namespace argument_parser { void set_program_name(std::string const &program_name); void set_parsed_arguments(std::vector const &parsed_arguments); }; - } + } // namespace v2 } // namespace argument_parser #endif diff --git a/src/headers/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp index 239ee65..7e8b8e4 100644 --- a/src/headers/parser/parser_v2.hpp +++ b/src/headers/parser/parser_v2.hpp @@ -15,7 +15,16 @@ #include namespace argument_parser::v2 { - enum class add_argument_flags { ShortArgument, LongArgument, Positional, Position, HelpText, Action, Required, Reference }; + enum class add_argument_flags { + ShortArgument, + LongArgument, + Positional, + Position, + HelpText, + Action, + Required, + Reference + }; namespace flags { constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument; @@ -30,7 +39,7 @@ namespace argument_parser::v2 { class base_parser : private argument_parser::base_parser { public: - template using typed_flag_value = std::variant, bool, int, T*>; + template using typed_flag_value = std::variant, bool, int, T *>; using non_typed_flag_value = std::variant; template using typed_argument_pair = std::pair>; @@ -148,21 +157,19 @@ namespace argument_parser::v2 { if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) { if (!IsTyped) { - throw std::logic_error("Reference argument must be typed"); + throw std::logic_error("Reference argument must be typed"); } found_params[extended_add_argument_flags::Action] = true; if constexpr (!std::is_same_v) { - auto ref = get_or_throw(argument_pairs.at(add_argument_flags::Reference), "reference"); + auto ref = get_or_throw(argument_pairs.at(add_argument_flags::Reference), "reference"); if (action) { - throw std::logic_error("Cannot use both action and reference for the same argument"); + throw std::logic_error("Cannot use both action and reference for the same argument"); } else { - action = helpers::make_parametered_action([ref](T const& t) { - *ref = t; - }).clone(); + action = helpers::make_parametered_action([ref](T const &t) { *ref = t; }).clone(); } } else { - throw std::logic_error("Reference argument must not be void"); + throw std::logic_error("Reference argument must not be void"); } } @@ -187,7 +194,8 @@ namespace argument_parser::v2 { } } - base::add_argument(short_arg, long_arg, help_text, *static_cast(&(*action)), required); + base::add_argument(short_arg, long_arg, help_text, *static_cast(&(*action)), + required); break; case candidate_type::store_other: if (help_text.empty()) { @@ -261,24 +269,21 @@ namespace argument_parser::v2 { if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) { if (!IsTyped) { - throw std::logic_error("Reference argument must be typed"); + throw std::logic_error("Reference argument must be typed"); } if constexpr (!std::is_same_v) { - auto ref = get_or_throw(argument_pairs.at(add_argument_flags::Reference), "reference"); + auto ref = get_or_throw(argument_pairs.at(add_argument_flags::Reference), "reference"); if (action) { - throw std::logic_error("Cannot use both action and reference for the same argument"); + throw std::logic_error("Cannot use both action and reference for the same argument"); } else { - action = helpers::make_parametered_action([ref](T const& t) { - *ref = t; - }).clone(); + action = helpers::make_parametered_action([ref](T const &t) { *ref = t; }).clone(); } } else { - throw std::logic_error("Reference argument must not be void"); + throw std::logic_error("Reference argument must not be void"); } } - if (help_text.empty()) { if constexpr (IsTyped) { if constexpr (internal::sfinae::has_format_hint>::value && @@ -297,8 +302,8 @@ namespace argument_parser::v2 { if constexpr (IsTyped) { if (action) { - base::add_positional_argument(positional_name, help_text, - *static_cast(&(*action)), required, position); + base::add_positional_argument(positional_name, help_text, *static_cast(&(*action)), + required, position); } else { base::template add_positional_argument(positional_name, help_text, required, position); } diff --git a/src/headers/parser/parsing_traits/traits.hpp b/src/headers/parser/parsing_traits/traits.hpp index 5e6bbfe..47ac636 100644 --- a/src/headers/parser/parsing_traits/traits.hpp +++ b/src/headers/parser/parsing_traits/traits.hpp @@ -5,12 +5,12 @@ #include namespace argument_parser::parsing_traits { - using hint_type = const char*; + using hint_type = const char *; template struct parser_trait { using type = T_; static T_ parse(const std::string &input); - static bool validate(T_ const&); + static bool validate(T_ const &); static constexpr hint_type format_hint = "value"; static constexpr hint_type purpose_hint = "value"; @@ -51,40 +51,36 @@ namespace argument_parser::parsing_traits { static constexpr hint_type purpose_hint = "double precision floating point number"; }; - - constexpr hint_type comma = ","; - template - struct hint_provider { - static constexpr hint_type value = *PtrAddr; - }; + template struct hint_provider { + static constexpr hint_type value = *PtrAddr; + }; - template - struct joiner { - static constexpr auto get_combined() { - constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0); + template struct joiner { + static constexpr auto get_combined() { + constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0); - std::array arr{}; - size_t offset = 0; + std::array arr{}; + size_t offset = 0; - auto append = [&](hint_type s) { - std::string_view sv{s}; - for (char c : sv) arr[offset++] = c; - return 0; - }; + auto append = [&](hint_type s) { + std::string_view sv{s}; + for (char c : sv) + arr[offset++] = c; + return 0; + }; - (append(Providers::value), ...); + (append(Providers::value), ...); - arr[total_len] = '\0'; - return arr; - } + arr[total_len] = '\0'; + return arr; + } - static constexpr auto storage = get_combined(); - static constexpr hint_type value = storage.data(); - }; + static constexpr auto storage = get_combined(); + static constexpr hint_type value = storage.data(); + }; - template - constexpr hint_type concat = joiner::value; + template constexpr hint_type concat = joiner::value; } // namespace argument_parser::parsing_traits diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 193d784..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include "macros.h" -#include "traits.hpp" -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct Point { - int x, y; -}; - -template <> struct argument_parser::parsing_traits::parser_trait { - static Point parse(const std::string &input) { - auto comma_pos = input.find(','); - if (comma_pos == std::string::npos) { - throw std::runtime_error("Invalid Point format. Expected 'x,y'."); - } - int x = std::stoi(input.substr(0, comma_pos)); - int y = std::stoi(input.substr(comma_pos + 1)); - return {x, y}; - } - - static bool validate(Point const& p) { - return p.x >= 0 && p.y >= 0; - } - - ARGPARSE_TRAIT_FORMAT_HINT = "x,y"; - ARGPARSE_TRAIT_PURPOSE_HINT = "coordinates"; -}; - -template <> struct argument_parser::parsing_traits::parser_trait { - static std::regex parse(const std::string &input) { - return std::regex(input); - } - ARGPARSE_TRAIT_FORMAT_HINT = "regex"; - ARGPARSE_TRAIT_PURPOSE_HINT = "regular expression"; -}; - - -template <> struct argument_parser::parsing_traits::parser_trait> { - static std::vector parse(const std::string &input) { - std::vector result; - std::stringstream ss{input}; - std::string item; - while (std::getline(ss, item, ',')) { - result.push_back(item); - } - return result; - } - - ARGPARSE_TRAIT_FORMAT_HINT = "string,string,string"; - ARGPARSE_TRAIT_PURPOSE_HINT = "list of strings"; -}; - -template struct argument_parser::parsing_traits::parser_trait> { - static std::vector parse(const std::string &input) { - std::vector result; - std::stringstream ss{input}; - std::string item; - - while (std::getline(ss, item, ',')) { - result.push_back(argument_parser::parsing_traits::parser_trait::parse(item)); - } - return result; - } - ARGPARSE_TRAIT_FORMAT_HINT = "VT,VT,VT"; - ARGPARSE_TRAIT_PURPOSE_HINT = "list of VT"; -}; - -const std::initializer_list conventions = { - &argument_parser::conventions::gnu_argument_convention, - &argument_parser::conventions::gnu_equal_argument_convention, - // &argument_parser::conventions::windows_argument_convention, - // &argument_parser::conventions::windows_equal_argument_convention -}; - -const auto echo = argument_parser::helpers::make_parametered_action( - [](std::string const &text) { std::cout << text << std::endl; }); - -const auto echo_point = argument_parser::helpers::make_parametered_action( - [](Point const &point) { std::cout << "Point(" << point.x << ", " << point.y << ")" << std::endl; }); - -const auto cat = argument_parser::helpers::make_parametered_action([](std::string const &file_name) { - std::ifstream file(file_name); - if (!file.is_open()) { - throw std::runtime_error("Could not open file"); - } - std::string line; - while (std::getline(file, line)) { - std::cout << line << std::endl; - } - - file.close(); -}); - - -auto grep(argument_parser::base_parser const &parser, std::string const &filename, std::regex const &pattern) { - if (filename.empty()) { - std::cerr << "Missing filename" << std::endl; - parser.display_help(conventions); - exit(-1); - } - std::ifstream file(filename); - if (!file.is_open()) { - std::cerr << "Could not open file: \"" << filename << '"' << std::endl; - exit(-1); - } - - for (std::string line; std::getline(file, line);) { - if (std::regex_search(line, pattern)) { - std::cout << line << std::endl; - } - } - - file.close(); -} - -void run_grep(argument_parser::base_parser const &parser) { - auto filename = parser.get_optional("file"); - auto pattern = parser.get_optional("grep"); - - if (filename && pattern) { - grep(parser, filename.value(), pattern.value()); - } else if (filename) { - std::cerr << "Missing grep pattern" << std::endl; - parser.display_help(conventions); - exit(-1); - } else if (pattern) { - std::cerr << "Missing filename" << std::endl; - parser.display_help(conventions); - exit(-1); - } -} - -void run_store_point(argument_parser::base_parser const &parser) { - auto point = parser.get_optional("store-point"); - if (point) { - std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl; - } -} - -int v2Examples() { - using namespace argument_parser::v2::flags; - argument_parser::v2::parser parser{ false }; - - parser.add_argument( - {{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}}); - - parser.add_argument({{ShortArgument, "ep"}, {LongArgument, "echo-point"}, {Action, echo_point}}); - - parser.add_argument({ - // stores string for f/file flag - {ShortArgument, "f"}, - {LongArgument, "file"}, - {HelpText, "File to grep, required only if using grep"}, - // if no action, falls to store operation with given type. - }); - - parser.add_argument({ - // stores string for g/grep flag - {ShortArgument, "g"}, - {LongArgument, "grep"}, - {HelpText, "Grep pattern, required only if using file"}, - // same as 'file' flag - }); - - parser.add_argument( - {{ShortArgument, "c"}, {LongArgument, "cat"}, {Action, cat}, {HelpText, "Prints the content of the file"}} - ); - - parser.add_argument({ - // { ShortArgument, "sp" }, // now if ShortArgument or LongArgument is missing, it will use it for the other. - {LongArgument, "store-point"}, - {Required, true} // makes this flag required - }); - - parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}}); - - parser.add_argument({ - {Positional, "input"}, - {HelpText, "Input file to process"}, - {Required, true}, - }); - - parser.add_argument({ - {Positional, "output"}, - {HelpText, "Output file path"}, - }); - - parser.add_argument>({{LongArgument, "points"}, {HelpText, "List of points to store"}}); - - parser.on_complete(::run_grep); - parser.on_complete(::run_store_point); - parser.on_complete([](argument_parser::base_parser const &p) { - auto input = p.get_optional("input"); - auto output = p.get_optional("output"); - if (input) { - std::cout << "Input: " << input.value() << std::endl; - } - if (output) { - std::cout << "Output: " << output.value() << std::endl; - } - }); - - parser.handle_arguments(conventions); - return 0; -} - -auto unique_copy(std::unique_ptr ptr) { - std::cout << *ptr << std::endl; -} - -auto unique_reference(std::unique_ptr const& ptr) { - std::cout << *ptr << std::endl; -} - -auto unique_move(std::unique_ptr&& ptr) { - std::cout << *ptr << std::endl; -} - -template -T return_example(std::function func) { - if constexpr (std::is_same_v) { - return func(); - } else { - return func(); - } -} - -template -void log_result(std::function func) { - if constexpr (std::is_same_v) { - func(); - } else { - std::cout << "result: " << func() << std::endl; - } -} - - -int main(int argc, char **argv) { - try { - return_example([]{}); - - return v2Examples(); - } catch (std::exception const &e) { - std::cout << e.what() << std::endl; - } -} diff --git a/src/source/conventions/base_convention.cpp b/src/source/conventions/base_convention.cpp index b8e483d..2214825 100644 --- a/src/source/conventions/base_convention.cpp +++ b/src/source/conventions/base_convention.cpp @@ -11,4 +11,4 @@ namespace argument_parser::conventions::helpers { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); return s; } -} // namespace argument_parser::conventions::helpers \ No newline at end of file +} // namespace argument_parser::conventions::helpers diff --git a/src/source/conventions/implementations/gnu_argument_convention.cpp b/src/source/conventions/implementations/gnu_argument_convention.cpp index 6bd704d..692d90c 100644 --- a/src/source/conventions/implementations/gnu_argument_convention.cpp +++ b/src/source/conventions/implementations/gnu_argument_convention.cpp @@ -123,4 +123,4 @@ namespace argument_parser::conventions::implementations { std::vector gnu_equal_argument_convention::get_features() const { return {}; // no fallback allowed } -} // namespace argument_parser::conventions::implementations \ No newline at end of file +} // namespace argument_parser::conventions::implementations diff --git a/src/source/conventions/implementations/windows_argument_convention.cpp b/src/source/conventions/implementations/windows_argument_convention.cpp index bfd9fd1..ab82c79 100644 --- a/src/source/conventions/implementations/windows_argument_convention.cpp +++ b/src/source/conventions/implementations/windows_argument_convention.cpp @@ -50,8 +50,9 @@ namespace argument_parser::conventions::implementations { return "/"; } - std::pair windows_argument_convention::make_help_text(std::string const &short_arg, std::string const &long_arg, - bool requires_value) const { + std::pair windows_argument_convention::make_help_text(std::string const &short_arg, + std::string const &long_arg, + bool requires_value) const { std::string s_part = ""; if (short_arg != "-" && short_arg != "") { s_part += short_prec() + short_arg; @@ -129,7 +130,8 @@ namespace argument_parser::conventions::implementations { } std::pair windows_kv_argument_convention::make_help_text(std::string const &short_arg, - std::string const &long_arg, bool requires_value) const { + std::string const &long_arg, + bool requires_value) const { std::string s_part = ""; if (short_arg != "-" && short_arg != "") { s_part += short_prec() + short_arg; @@ -153,4 +155,4 @@ namespace argument_parser::conventions::implementations { return {convention_features::ALLOW_LONG_TO_SHORT_FALLBACK, convention_features::ALLOW_SHORT_TO_LONG_FALLBACK}; // interchangable } -} // namespace argument_parser::conventions::implementations \ No newline at end of file +} // namespace argument_parser::conventions::implementations diff --git a/src/source/parser/argument_parser.cpp b/src/source/parser/argument_parser.cpp index 31e9507..509a289 100644 --- a/src/source/parser/argument_parser.cpp +++ b/src/source/parser/argument_parser.cpp @@ -530,4 +530,4 @@ namespace argument_parser { event(*this); } } -} // namespace argument_parser \ No newline at end of file +} // namespace argument_parser diff --git a/src/source/parser/fake_parser.cpp b/src/source/parser/fake_parser.cpp index d0a263c..c743a8c 100644 --- a/src/source/parser/fake_parser.cpp +++ b/src/source/parser/fake_parser.cpp @@ -23,28 +23,27 @@ namespace argument_parser { } namespace v2 { - fake_parser::fake_parser(std::string program_name, std::vector const &arguments) { - set_program_name(program_name); - ref_parsed_args() = arguments; - prepare_help_flag(false); - } + fake_parser::fake_parser(std::string program_name, std::vector const &arguments) { + set_program_name(program_name); + ref_parsed_args() = arguments; + prepare_help_flag(false); + } - fake_parser::fake_parser(std::string const &program_name, std::vector &&arguments) { - set_program_name(program_name); - ref_parsed_args() = std::move(arguments); - prepare_help_flag(false); - } + fake_parser::fake_parser(std::string const &program_name, std::vector &&arguments) { + set_program_name(program_name); + ref_parsed_args() = std::move(arguments); + prepare_help_flag(false); + } - fake_parser::fake_parser(std::string const &program_name, std::initializer_list const &arguments) - : fake_parser(program_name, std::vector(arguments)) { - } + fake_parser::fake_parser(std::string const &program_name, std::initializer_list const &arguments) + : fake_parser(program_name, std::vector(arguments)) {} - void fake_parser::set_program_name(std::string const &program_name) { - set_program_name(program_name); - } + void fake_parser::set_program_name(std::string const &program_name) { + set_program_name(program_name); + } - void fake_parser::set_parsed_arguments(std::vector const &parsed_arguments) { - ref_parsed_args() = parsed_arguments; - } - } + void fake_parser::set_parsed_arguments(std::vector const &parsed_arguments) { + ref_parsed_args() = parsed_arguments; + } + } // namespace v2 } // namespace argument_parser diff --git a/src/source/parser/parsing_traits/traits.cpp b/src/source/parser/parsing_traits/traits.cpp index fb4ed89..731cec2 100644 --- a/src/source/parser/parsing_traits/traits.cpp +++ b/src/source/parser/parsing_traits/traits.cpp @@ -25,4 +25,4 @@ namespace argument_parser::parsing_traits { double parser_trait::parse(const std::string &input) { return std::stod(input); } -} // namespace argument_parser::parsing_traits \ No newline at end of file +} // namespace argument_parser::parsing_traits diff --git a/src/source/parser/platform_parsers/macos_parser.cpp b/src/source/parser/platform_parsers/macos_parser.cpp index d2b473c..a7c48d4 100644 --- a/src/source/parser/platform_parsers/macos_parser.cpp +++ b/src/source/parser/platform_parsers/macos_parser.cpp @@ -4,15 +4,17 @@ #include -#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \ - do { \ - const int argc_name = *_NSGetArgc(); \ - if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \ - do { before_for; } while(false); \ - for (int i = 1; i < argc_name; ++i) { \ - for_body \ - } \ - } \ +#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \ + do { \ + const int argc_name = *_NSGetArgc(); \ + if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \ + do { \ + before_for; \ + } while (false); \ + for (int i = 1; i < argc_name; ++i) { \ + for_body \ + } \ + } \ } while (false) namespace argument_parser { @@ -25,10 +27,10 @@ namespace argument_parser { namespace v2 { macos_parser::macos_parser(bool should_exit) { - MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), { - if (argv[i] != nullptr) + MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), { + if (argv[i] != nullptr) ref_parsed_args().emplace_back(argv[i]); - }); + }); prepare_help_flag(should_exit); } } // namespace v2 diff --git a/src/test.cpp b/src/test.cpp deleted file mode 100644 index 6a2abe6..0000000 --- a/src/test.cpp +++ /dev/null @@ -1,420 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -const std::initializer_list conventions = { - &argument_parser::conventions::gnu_argument_convention, - &argument_parser::conventions::gnu_equal_argument_convention, -}; - -namespace v2_test { - class fake_parser : public argument_parser::v2::base_parser { - public: - fake_parser(std::string const &program_name, std::initializer_list const &arguments) { - set_program_name(program_name); - ref_parsed_args() = std::vector(arguments); - prepare_help_flag(); - } - }; -} // namespace v2_test - -int tests_run = 0; -int tests_passed = 0; - -void test_result(const char *name, bool passed) { - tests_run++; - if (passed) { - tests_passed++; - std::cout << " [PASS] " << name << std::endl; - } else { - std::cout << " [FAIL] " << name << std::endl; - } -} - -// ============================================================ -// V1 Tests (using argument_parser::fake_parser) -// ============================================================ - -void test_v1_single_positional_store() { - argument_parser::fake_parser parser("test", {"hello"}); - parser.add_positional_argument("greeting", "A greeting", false); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("greeting"); - test_result("v1: single positional store", val.has_value() && val.value() == "hello"); -} - -void test_v1_multiple_positionals_ordered() { - argument_parser::fake_parser parser("test", {"alpha", "beta", "gamma"}); - parser.add_positional_argument("first", "First arg", false); - parser.add_positional_argument("second", "Second arg", false); - parser.add_positional_argument("third", "Third arg", false); - parser.handle_arguments(conventions); - - auto first = parser.get_optional("first"); - auto second = parser.get_optional("second"); - auto third = parser.get_optional("third"); - - bool ok = first.has_value() && first.value() == "alpha" && second.has_value() && second.value() == "beta" && - third.has_value() && third.value() == "gamma"; - test_result("v1: multiple positionals preserve order", ok); -} - -void test_v1_positional_with_explicit_position() { - argument_parser::fake_parser parser("test", {"first_val", "second_val"}); - parser.add_positional_argument("second", "Second", false, 1); - parser.add_positional_argument("first", "First", false, 0); - parser.handle_arguments(conventions); - - auto first = parser.get_optional("first"); - auto second = parser.get_optional("second"); - - bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val"; - test_result("v1: explicit position index", ok); -} - -void test_v1_positional_typed_int() { - argument_parser::fake_parser parser("test", {"42"}); - parser.add_positional_argument("count", "A count", false); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("count"); - test_result("v1: positional with int type", val.has_value() && val.value() == 42); -} - -void test_v1_positional_with_action() { - std::string captured; - argument_parser::fake_parser parser("test", {"world"}); - - auto action = - argument_parser::helpers::make_parametered_action([&](std::string const &v) { captured = v; }); - parser.add_positional_argument("name", "A name", action, false); - parser.handle_arguments(conventions); - - test_result("v1: positional with action", captured == "world"); -} - -void test_v1_mixed_named_and_positional() { - argument_parser::fake_parser parser("test", {"--verbose", "true", "myfile.txt"}); - parser.add_argument("v", "verbose", "Verbose mode", false); - parser.add_positional_argument("file", "Input file", false); - parser.handle_arguments(conventions); - - auto verbose = parser.get_optional("verbose"); - auto file = parser.get_optional("file"); - - bool ok = verbose.has_value() && verbose.value() == true && file.has_value() && file.value() == "myfile.txt"; - test_result("v1: mixed named and positional args", ok); -} - -void test_v1_positional_after_named() { - argument_parser::fake_parser parser("test", {"-n", "5", "output.txt"}); - parser.add_argument("n", "number", "A number", false); - parser.add_positional_argument("output", "Output file", false); - parser.handle_arguments(conventions); - - auto number = parser.get_optional("number"); - auto output = parser.get_optional("output"); - - bool ok = number.has_value() && number.value() == 5 && output.has_value() && output.value() == "output.txt"; - test_result("v1: positional after named args", ok); -} - -void test_v1_positional_between_named() { - argument_parser::fake_parser parser("test", {"-a", "1", "positional_val", "--beta", "2"}); - parser.add_argument("a", "alpha", "Alpha", false); - parser.add_argument("b", "beta", "Beta", false); - parser.add_positional_argument("middle", "Middle arg", false); - parser.handle_arguments(conventions); - - auto alpha = parser.get_optional("alpha"); - auto beta = parser.get_optional("beta"); - auto middle = parser.get_optional("middle"); - - bool ok = alpha.has_value() && alpha.value() == 1 && beta.has_value() && beta.value() == 2 && middle.has_value() && - middle.value() == "positional_val"; - test_result("v1: positional between named args", ok); -} - -void test_v1_double_dash_separator() { - argument_parser::fake_parser parser("test", {"--", "-not-a-flag"}); - parser.add_positional_argument("item", "An item", false); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("item"); - test_result("v1: -- separator treats next as positional", val.has_value() && val.value() == "-not-a-flag"); -} - -void test_v1_double_dash_multiple() { - argument_parser::fake_parser parser("test", {"--name", "hello", "--", "--weird", "-x"}); - parser.add_argument("n", "name", "A name", false); - parser.add_positional_argument("first", "First", false); - parser.add_positional_argument("second", "Second", false); - parser.handle_arguments(conventions); - - auto name = parser.get_optional("name"); - auto first = parser.get_optional("first"); - auto second = parser.get_optional("second"); - - bool ok = name.has_value() && name.value() == "hello" && first.has_value() && first.value() == "--weird" && - second.has_value() && second.value() == "-x"; - test_result("v1: -- separator with multiple positionals", ok); -} - -void test_v1_required_positional_missing() { - argument_parser::fake_parser parser("test", {}); - parser.add_positional_argument("file", "A file", true); - - bool threw = false; - try { - // check_for_required_arguments calls std::exit(1) so we can't easily test it - // instead, test that handle_arguments doesn't crash when positionals are provided - parser.handle_arguments(conventions); - } catch (...) { - threw = true; - } - // Note: required check calls std::exit(1), so if we get here the arg wasn't required-checked - // This test just verifies setup doesn't crash. The exit behavior is tested manually. - test_result("v1: required positional setup (no crash)", true); -} - -void test_v1_unexpected_positional_throws() { - argument_parser::fake_parser parser("test", {"unexpected"}); - // no positional args defined, but a bare token is provided - - bool threw = false; - try { - parser.handle_arguments(conventions); - } catch (const std::runtime_error &) { - threw = true; - } - test_result("v1: unexpected positional throws", threw); -} - -void test_v1_duplicate_positional_name_throws() { - argument_parser::fake_parser parser("test", {"a", "b"}); - parser.add_positional_argument("file", "A file", false); - - bool threw = false; - try { - parser.add_positional_argument("file", "Duplicate", false); - } catch (const std::runtime_error &) { - threw = true; - } - test_result("v1: duplicate positional name throws", threw); -} - -void test_v1_positional_on_complete() { - std::string captured_file; - argument_parser::fake_parser parser("test", {"data.csv"}); - parser.add_positional_argument("file", "Input file", false); - parser.on_complete([&](argument_parser::base_parser const &p) { - auto val = p.get_optional("file"); - if (val) - captured_file = val.value(); - }); - parser.handle_arguments(conventions); - - test_result("v1: positional accessible in on_complete", captured_file == "data.csv"); -} - -// ============================================================ -// V2 Tests (using v2_test::fake_parser) -// ============================================================ - -void test_v2_single_positional() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"hello"}); - - parser.add_argument({{Positional, "greeting"}, {HelpText, "A greeting"}}); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("greeting"); - test_result("v2: single positional store", val.has_value() && val.value() == "hello"); -} - -void test_v2_positional_required() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"value"}); - - parser.add_argument({{Positional, "arg"}, {Required, true}, {HelpText, "Required arg"}}); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("arg"); - test_result("v2: required positional", val.has_value() && val.value() == "value"); -} - -void test_v2_positional_with_position() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"first_val", "second_val"}); - - parser.add_argument({{Positional, "second"}, {Position, 1}, {HelpText, "Second"}}); - parser.add_argument({{Positional, "first"}, {Position, 0}, {HelpText, "First"}}); - parser.handle_arguments(conventions); - - auto first = parser.get_optional("first"); - auto second = parser.get_optional("second"); - - bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val"; - test_result("v2: positional with explicit Position", ok); -} - -void test_v2_positional_typed_int() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"99"}); - - parser.add_argument({{Positional, "count"}, {HelpText, "A count"}}); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("count"); - test_result("v2: positional with int type", val.has_value() && val.value() == 99); -} - -void test_v2_mixed_named_and_positional() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"--output", "out.txt", "input.txt"}); - - parser.add_argument({{ShortArgument, "o"}, {LongArgument, "output"}, {HelpText, "Output file"}}); - parser.add_argument({{Positional, "input"}, {HelpText, "Input file"}}); - parser.handle_arguments(conventions); - - auto output = parser.get_optional("output"); - auto input = parser.get_optional("input"); - - bool ok = output.has_value() && output.value() == "out.txt" && input.has_value() && input.value() == "input.txt"; - test_result("v2: mixed named and positional", ok); -} - -void test_v2_positional_with_action() { - using namespace argument_parser::v2::flags; - std::string captured; - v2_test::fake_parser parser("test", {"world"}); - - parser.add_argument({{Positional, "name"}, - {Action, argument_parser::helpers::make_parametered_action( - [&](std::string const &v) { captured = v; })}, - {HelpText, "A name"}}); - parser.handle_arguments(conventions); - - test_result("v2: positional with action", captured == "world"); -} - -void test_v2_double_dash_separator() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"--", "--not-a-flag"}); - - parser.add_argument({{Positional, "item"}, {HelpText, "An item"}}); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("item"); - test_result("v2: -- separator", val.has_value() && val.value() == "--not-a-flag"); -} - -void test_v2_positional_auto_help_text() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"42"}); - - // no HelpText provided — should auto-generate from traits - parser.add_argument({{Positional, "count"}}); - parser.handle_arguments(conventions); - - auto val = parser.get_optional("count"); - test_result("v2: positional auto help text (no crash)", val.has_value() && val.value() == 42); -} - -void test_v2_multiple_positionals_and_named() { - using namespace argument_parser::v2::flags; - v2_test::fake_parser parser("test", {"-v", "src.txt", "dst.txt"}); - - parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}}); - parser.add_argument({{Positional, "source"}, {HelpText, "Source"}}); - parser.add_argument({{Positional, "destination"}, {HelpText, "Destination"}}); - parser.handle_arguments(conventions); - - auto verbose = parser.get_optional("verbose"); - auto source = parser.get_optional("source"); - auto dest = parser.get_optional("destination"); - - bool ok = verbose.has_value() && source.has_value() && source.value() == "src.txt" && dest.has_value() && - dest.value() == "dst.txt"; - test_result("v2: multiple positionals with named flag", ok); -} - -void test_v2_on_complete_with_positional() { - using namespace argument_parser::v2::flags; - std::string captured; - v2_test::fake_parser parser("test", {"payload"}); - - parser.add_argument({{Positional, "data"}, {HelpText, "Data"}}); - parser.on_complete([&](argument_parser::base_parser const &p) { - auto val = p.get_optional("data"); - if (val) - captured = val.value(); - }); - parser.handle_arguments(conventions); - - test_result("v2: positional accessible in on_complete", captured == "payload"); -} - -// ============================================================ -// Main -// ============================================================ - -int main() { - std::cout << "=== V1 Positional Argument Tests ===" << std::endl; - - std::array, 13> v1Tests { - test_v1_single_positional_store, - test_v1_multiple_positionals_ordered, - test_v1_positional_with_explicit_position, - test_v1_positional_typed_int, - test_v1_positional_with_action, - test_v1_mixed_named_and_positional, - test_v1_positional_after_named, - test_v1_positional_between_named, - test_v1_double_dash_separator, - test_v1_double_dash_multiple, - test_v1_unexpected_positional_throws, - test_v1_duplicate_positional_name_throws, - test_v1_positional_on_complete - }; - - for (auto const& test : v1Tests) { - try { - test(); - } catch(std::exception const& e) { - std::cout << "test failed: " << e.what() << std::endl; - } - } - - std::cout << "\n=== V2 Positional Argument Tests ===" << std::endl; - std::array, 10> v2Tests{ - test_v2_single_positional, - test_v2_positional_required, - test_v2_positional_with_position, - test_v2_positional_typed_int, - test_v2_mixed_named_and_positional, - test_v2_positional_with_action, - test_v2_double_dash_separator, - test_v2_positional_auto_help_text, - test_v2_multiple_positionals_and_named, - test_v2_on_complete_with_positional - }; - - - for (auto const& test : v2Tests) { - try { - test(); - } catch(std::exception const& e) { - std::cout << "test failed: " << e.what() << std::endl; - } - } - - std::cout << "\n=== Results: " << tests_passed << "/" << tests_run << " passed ===" << std::endl; - return (tests_passed == tests_run) ? 0 : 1; -}