diff --git a/src/headers/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp index eb55d73..799a088 100644 --- a/src/headers/parser/argument_parser.hpp +++ b/src/headers/parser/argument_parser.hpp @@ -19,7 +19,6 @@ #include #include - namespace argument_parser { namespace internal::atomic { template class copyable_atomic { @@ -246,6 +245,21 @@ namespace argument_parser { } private: + bool test_conventions(std::initializer_list convention_types, + std::unordered_map &values_for_arguments, + std::vector> &found_arguments, + std::optional &found_help, std::vector::iterator it, + std::stringstream &error_stream); + void extract_arguments(std::initializer_list convention_types, + std::unordered_map &values_for_arguments, + std::vector> &found_arguments, + std::optional &found_help); + + void invoke_arguments(std::unordered_map const &values_for_arguments, + std::vector> const &found_arguments, + std::optional const &found_help); + void enforce_creation_thread(); + void assert_argument_not_exist(std::string const &short_arg, std::string const &long_arg) const; static void set_argument_status(bool is_required, std::string const &help_text, argument &arg); void place_argument(int id, argument const &arg, std::string const &short_arg, std::string const &long_arg); diff --git a/src/headers/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp index 3fdbaac..0298805 100644 --- a/src/headers/parser/parser_v2.hpp +++ b/src/headers/parser/parser_v2.hpp @@ -92,6 +92,16 @@ namespace argument_parser::v2 { using argument_parser::base_parser::current_conventions; using argument_parser::base_parser::reset_current_conventions; + void prepare_help_flag() { + add_argument({{flags::ShortArgument, "h"}, + {flags::LongArgument, "help"}, + {flags::Action, helpers::make_non_parametered_action([this]() { + this->display_help(this->current_conventions()); + std::exit(0); + })}, + {flags::HelpText, "Prints this help text."}}); + } + private: template void add_argument_impl(ArgsMap const &argument_pairs) { diff --git a/src/source/parser/argument_parser.cpp b/src/source/parser/argument_parser.cpp index 5e65982..a14ed97 100644 --- a/src/source/parser/argument_parser.cpp +++ b/src/source/parser/argument_parser.cpp @@ -1,8 +1,12 @@ #include "argument_parser.hpp" +#include #include +#include #include #include +#include +#include class deferred_exec { public: @@ -112,54 +116,112 @@ namespace argument_parser { throw std::runtime_error("Unknown argument: " + arg.second); } - void base_parser::handle_arguments(std::initializer_list convention_types) { + void base_parser::enforce_creation_thread() { if (std::this_thread::get_id() != this->creation_thread_id.load()) { throw std::runtime_error("handle_arguments must be called from the main thread"); } + } - deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); }); - this->current_conventions(convention_types); + bool base_parser::test_conventions(std::initializer_list convention_types, + std::unordered_map &values_for_arguments, + std::vector> &found_arguments, + std::optional &found_help, std::vector::iterator it, + std::stringstream &error_stream) { - for (auto it = parsed_arguments.begin(); it != parsed_arguments.end(); ++it) { - std::stringstream error_stream; - bool arg_correctly_handled = false; + std::string current_argument = *it; - for (auto const &convention_type : convention_types) { - auto extracted = convention_type->get_argument(*it); - if (extracted.first == conventions::argument_type::ERROR) { - error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << extracted.second - << "\n"; + for (auto const &convention_type : convention_types) { + auto extracted = convention_type->get_argument(current_argument); + if (extracted.first == conventions::argument_type::ERROR) { + error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << extracted.second + << "\n"; + continue; + } + + try { + argument &corresponding_argument = get_argument(extracted); + + if (extracted.second == "h" || extracted.second == "help") { + found_help = corresponding_argument; continue; } - try { - argument &corresponding_argument = get_argument(extracted); - if (corresponding_argument.expects_parameter()) { - if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) { - throw std::runtime_error("expected value for argument " + extracted.second); - } - auto value_raw = - convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it); - corresponding_argument.action->invoke_with_parameter(value_raw); - } else { - corresponding_argument.action->invoke(); + found_arguments.emplace_back(extracted.second, corresponding_argument); + + if (corresponding_argument.expects_parameter()) { + if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) { + throw std::runtime_error("expected value for argument " + extracted.second); } - - corresponding_argument.set_invoked(true); - arg_correctly_handled = true; - break; // Convention succeeded, move to the next argument token - - } catch (const std::runtime_error &e) { - error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what() - << "\n"; + values_for_arguments[extracted.second] = + convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it); } - } - if (!arg_correctly_handled) { + corresponding_argument.set_invoked(true); + return true; + } catch (const std::runtime_error &e) { + error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what() << "\n"; + } + } + + return false; + } + + void base_parser::extract_arguments(std::initializer_list convention_types, + std::unordered_map &values_for_arguments, + std::vector> &found_arguments, + std::optional &found_help) { + + for (auto it = parsed_arguments.begin(); it != parsed_arguments.end(); ++it) { + std::stringstream error_stream; + + if (!test_conventions(convention_types, values_for_arguments, found_arguments, found_help, it, + error_stream)) { throw std::runtime_error("All trials for argument: \n\t\"" + *it + "\"\n failed with: \n" + error_stream.str()); } } + } + + void base_parser::invoke_arguments(std::unordered_map const &values_for_arguments, + std::vector> const &found_arguments, + std::optional const &found_help) { + + if (found_help) { + found_help->action->invoke(); + return; + } + + std::stringstream error_stream; + for (auto const &[key, value] : found_arguments) { + try { + if (value.expects_parameter()) { + value.action->invoke_with_parameter(values_for_arguments.at(key)); + } else { + value.action->invoke(); + } + } catch (const std::runtime_error &e) { + error_stream << "Argument " << key << " failed with: " << e.what() << "\n"; + } + } + + std::string error_message = error_stream.str(); + if (!error_message.empty()) { + throw std::runtime_error(error_message); + } + } + + void base_parser::handle_arguments(std::initializer_list convention_types) { + enforce_creation_thread(); + + deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); }); + this->current_conventions(convention_types); + + std::unordered_map values_for_arguments; + std::vector> found_arguments; + std::optional found_help = std::nullopt; + + extract_arguments(convention_types, values_for_arguments, found_arguments, found_help); + invoke_arguments(values_for_arguments, found_arguments, found_help); check_for_required_arguments(convention_types); fire_on_complete_events(); } diff --git a/src/source/parser/platform_parsers/linux_parser.cpp b/src/source/parser/platform_parsers/linux_parser.cpp index 7d56d53..56b7c44 100644 --- a/src/source/parser/platform_parsers/linux_parser.cpp +++ b/src/source/parser/platform_parsers/linux_parser.cpp @@ -25,13 +25,7 @@ namespace argument_parser { parsed_arguments.emplace_back(line); } - add_argument({{flags::ShortArgument, "h"}, - {flags::LongArgument, "help"}, - {flags::Action, helpers::make_non_parametered_action([this]() { - this->display_help(this->current_conventions()); - std::exit(0); - })}, - {flags::HelpText, "Prints this help text."}}); + prepare_help_flag(); } } // namespace v2 } // namespace argument_parser diff --git a/src/source/parser/platform_parsers/macos_parser.cpp b/src/source/parser/platform_parsers/macos_parser.cpp index 22ede10..4b3c60e 100644 --- a/src/source/parser/platform_parsers/macos_parser.cpp +++ b/src/source/parser/platform_parsers/macos_parser.cpp @@ -25,13 +25,7 @@ namespace argument_parser { } } - add_argument({{flags::ShortArgument, "h"}, - {flags::LongArgument, "help"}, - {flags::Action, helpers::make_non_parametered_action([this]() { - this->display_help(this->current_conventions()); - std::exit(0); - })}, - {flags::HelpText, "Prints this help text."}}); + prepare_help_flag(); } } // namespace v2 } // namespace argument_parser diff --git a/src/source/parser/platform_parsers/windows_parser.cpp b/src/source/parser/platform_parsers/windows_parser.cpp index 4b0003c..c557ae3 100644 --- a/src/source/parser/platform_parsers/windows_parser.cpp +++ b/src/source/parser/platform_parsers/windows_parser.cpp @@ -3,7 +3,6 @@ #include "windows_parser.hpp" #include "argument_parser.hpp" -#include "parser_v2.hpp" #include #include @@ -100,13 +99,7 @@ namespace argument_parser::v2 { parse_windows_arguments(ref_parsed_args(), [this](std::string const &program_name) { this->set_program_name(program_name); }); - add_argument({{flags::ShortArgument, "h"}, - {flags::LongArgument, "help"}, - {flags::Action, helpers::make_non_parametered_action([this]() { - this->display_help(this->current_conventions()); - std::exit(0); - })}, - {flags::HelpText, "Prints this help text."}}); + prepare_help_flag(); } } // namespace argument_parser::v2