mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-04-13 03:41:18 +00:00
chore: deduplicate builtin help action generation.
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace argument_parser {
|
||||
namespace internal::atomic {
|
||||
template <typename T> class copyable_atomic {
|
||||
@@ -246,6 +245,21 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
private:
|
||||
bool test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
|
||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
||||
std::optional<argument> &found_help, std::vector<std::string>::iterator it,
|
||||
std::stringstream &error_stream);
|
||||
void extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
|
||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
||||
std::optional<argument> &found_help);
|
||||
|
||||
void invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> const &found_arguments,
|
||||
std::optional<argument> 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);
|
||||
|
||||
@@ -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 <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
||||
void add_argument_impl(ArgsMap const &argument_pairs) {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include "argument_parser.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class deferred_exec {
|
||||
public:
|
||||
@@ -112,20 +116,22 @@ namespace argument_parser {
|
||||
throw std::runtime_error("Unknown argument: " + arg.second);
|
||||
}
|
||||
|
||||
void base_parser::handle_arguments(std::initializer_list<conventions::convention const *const> 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<conventions::convention const *const> convention_types,
|
||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
||||
std::optional<argument> &found_help, std::vector<std::string>::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);
|
||||
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";
|
||||
@@ -134,32 +140,88 @@ namespace argument_parser {
|
||||
|
||||
try {
|
||||
argument &corresponding_argument = get_argument(extracted);
|
||||
|
||||
if (extracted.second == "h" || extracted.second == "help") {
|
||||
found_help = corresponding_argument;
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
auto value_raw =
|
||||
values_for_arguments[extracted.second] =
|
||||
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
||||
corresponding_argument.action->invoke_with_parameter(value_raw);
|
||||
} else {
|
||||
corresponding_argument.action->invoke();
|
||||
}
|
||||
|
||||
corresponding_argument.set_invoked(true);
|
||||
arg_correctly_handled = true;
|
||||
break; // Convention succeeded, move to the next argument token
|
||||
|
||||
return true;
|
||||
} catch (const std::runtime_error &e) {
|
||||
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what()
|
||||
<< "\n";
|
||||
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_correctly_handled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void base_parser::extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
|
||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
||||
std::optional<argument> &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<std::string, std::string> const &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> const &found_arguments,
|
||||
std::optional<argument> 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<conventions::convention const *const> convention_types) {
|
||||
enforce_creation_thread();
|
||||
|
||||
deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); });
|
||||
this->current_conventions(convention_types);
|
||||
|
||||
std::unordered_map<std::string, std::string> values_for_arguments;
|
||||
std::vector<std::pair<std::string, argument>> found_arguments;
|
||||
std::optional<argument> 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "windows_parser.hpp"
|
||||
#include "argument_parser.hpp"
|
||||
#include "parser_v2.hpp"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user