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 <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace argument_parser {
|
namespace argument_parser {
|
||||||
namespace internal::atomic {
|
namespace internal::atomic {
|
||||||
template <typename T> class copyable_atomic {
|
template <typename T> class copyable_atomic {
|
||||||
@@ -246,6 +245,21 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
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);
|
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);
|
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::current_conventions;
|
||||||
using argument_parser::base_parser::reset_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:
|
private:
|
||||||
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
||||||
void add_argument_impl(ArgsMap const &argument_pairs) {
|
void add_argument_impl(ArgsMap const &argument_pairs) {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
#include "argument_parser.hpp"
|
#include "argument_parser.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class deferred_exec {
|
class deferred_exec {
|
||||||
public:
|
public:
|
||||||
@@ -112,54 +116,112 @@ namespace argument_parser {
|
|||||||
throw std::runtime_error("Unknown argument: " + arg.second);
|
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()) {
|
if (std::this_thread::get_id() != this->creation_thread_id.load()) {
|
||||||
throw std::runtime_error("handle_arguments must be called from the main thread");
|
throw std::runtime_error("handle_arguments must be called from the main thread");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); });
|
bool base_parser::test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
|
||||||
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::vector<std::string>::iterator it,
|
||||||
|
std::stringstream &error_stream) {
|
||||||
|
|
||||||
for (auto it = parsed_arguments.begin(); it != parsed_arguments.end(); ++it) {
|
std::string current_argument = *it;
|
||||||
std::stringstream error_stream;
|
|
||||||
bool arg_correctly_handled = false;
|
|
||||||
|
|
||||||
for (auto const &convention_type : convention_types) {
|
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) {
|
if (extracted.first == conventions::argument_type::ERROR) {
|
||||||
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << extracted.second
|
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << extracted.second
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
argument &corresponding_argument = get_argument(extracted);
|
||||||
|
|
||||||
|
if (extracted.second == "h" || extracted.second == "help") {
|
||||||
|
found_help = corresponding_argument;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
found_arguments.emplace_back(extracted.second, corresponding_argument);
|
||||||
argument &corresponding_argument = get_argument(extracted);
|
|
||||||
if (corresponding_argument.expects_parameter()) {
|
if (corresponding_argument.expects_parameter()) {
|
||||||
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
|
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
|
||||||
throw std::runtime_error("expected value for argument " + extracted.second);
|
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();
|
|
||||||
}
|
}
|
||||||
|
values_for_arguments[extracted.second] =
|
||||||
corresponding_argument.set_invoked(true);
|
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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<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" +
|
throw std::runtime_error("All trials for argument: \n\t\"" + *it + "\"\n failed with: \n" +
|
||||||
error_stream.str());
|
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);
|
check_for_required_arguments(convention_types);
|
||||||
fire_on_complete_events();
|
fire_on_complete_events();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,7 @@ namespace argument_parser {
|
|||||||
parsed_arguments.emplace_back(line);
|
parsed_arguments.emplace_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_argument({{flags::ShortArgument, "h"},
|
prepare_help_flag();
|
||||||
{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."}});
|
|
||||||
}
|
}
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -25,13 +25,7 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_argument({{flags::ShortArgument, "h"},
|
prepare_help_flag();
|
||||||
{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."}});
|
|
||||||
}
|
}
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "windows_parser.hpp"
|
#include "windows_parser.hpp"
|
||||||
#include "argument_parser.hpp"
|
#include "argument_parser.hpp"
|
||||||
#include "parser_v2.hpp"
|
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -100,13 +99,7 @@ namespace argument_parser::v2 {
|
|||||||
parse_windows_arguments(ref_parsed_args(),
|
parse_windows_arguments(ref_parsed_args(),
|
||||||
[this](std::string const &program_name) { this->set_program_name(program_name); });
|
[this](std::string const &program_name) { this->set_program_name(program_name); });
|
||||||
|
|
||||||
add_argument({{flags::ShortArgument, "h"},
|
prepare_help_flag();
|
||||||
{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."}});
|
|
||||||
}
|
}
|
||||||
} // namespace argument_parser::v2
|
} // namespace argument_parser::v2
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user