mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-07-14 09:38:11 +00:00
feat: introduce reference return, accumulators. chore: lint
This commit is contained in:
@@ -3,12 +3,12 @@
|
||||
|
||||
namespace argument_parser::conventions::helpers {
|
||||
std::string to_lower(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](const unsigned char c) { return std::tolower(c); });
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string to_upper(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](const unsigned char c) { return std::toupper(c); });
|
||||
return s;
|
||||
}
|
||||
} // namespace argument_parser::conventions::helpers
|
||||
|
||||
@@ -10,10 +10,9 @@ namespace argument_parser::conventions::implementations {
|
||||
parsed_argument gnu_argument_convention::get_argument(std::string const &raw) const {
|
||||
if (starts_with(raw, long_prec()))
|
||||
return {argument_type::LONG, raw.substr(2)};
|
||||
else if (starts_with(raw, short_prec()))
|
||||
if (starts_with(raw, short_prec()))
|
||||
return {argument_type::SHORT, raw.substr(1)};
|
||||
else
|
||||
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
|
||||
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
|
||||
}
|
||||
|
||||
std::string gnu_argument_convention::extract_value(std::string const & /*raw*/) const {
|
||||
@@ -42,17 +41,17 @@ namespace argument_parser::conventions::implementations {
|
||||
|
||||
std::pair<std::string, std::string> gnu_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 != "") {
|
||||
bool const requires_value) const {
|
||||
std::string s_part;
|
||||
if (short_arg != "-" && !short_arg.empty()) {
|
||||
s_part += short_prec() + short_arg;
|
||||
if (requires_value) {
|
||||
s_part += " <value>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string l_part = "";
|
||||
if (long_arg != "-" && long_arg != "") {
|
||||
std::string l_part;
|
||||
if (long_arg != "-" && !long_arg.empty()) {
|
||||
l_part += long_prec() + long_arg;
|
||||
if (requires_value) {
|
||||
l_part += " <value>";
|
||||
@@ -65,18 +64,17 @@ namespace argument_parser::conventions::implementations {
|
||||
|
||||
namespace argument_parser::conventions::implementations {
|
||||
parsed_argument gnu_equal_argument_convention::get_argument(std::string const &raw) const {
|
||||
auto pos = raw.find('=');
|
||||
auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
|
||||
const auto pos = raw.find('=');
|
||||
const auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
|
||||
if (starts_with(arg, long_prec()))
|
||||
return {argument_type::LONG, arg.substr(2)};
|
||||
else if (starts_with(arg, short_prec()))
|
||||
if (starts_with(arg, short_prec()))
|
||||
return {argument_type::SHORT, arg.substr(1)};
|
||||
else
|
||||
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
|
||||
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
|
||||
}
|
||||
|
||||
std::string gnu_equal_argument_convention::extract_value(std::string const &raw) const {
|
||||
auto pos = raw.find('=');
|
||||
const auto pos = raw.find('=');
|
||||
if (pos == std::string::npos || pos + 1 >= raw.size())
|
||||
throw std::runtime_error("Expected value after '='.");
|
||||
return raw.substr(pos + 1);
|
||||
@@ -100,17 +98,17 @@ namespace argument_parser::conventions::implementations {
|
||||
|
||||
std::pair<std::string, std::string> gnu_equal_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 != "") {
|
||||
bool const requires_value) const {
|
||||
std::string s_part;
|
||||
if (short_arg != "-" && !short_arg.empty()) {
|
||||
s_part += short_prec() + short_arg;
|
||||
if (requires_value) {
|
||||
s_part += "=<value>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string l_part = "";
|
||||
if (long_arg != "-" && long_arg != "") {
|
||||
std::string l_part;
|
||||
if (long_arg != "-" && !long_arg.empty()) {
|
||||
l_part += long_prec() + long_arg;
|
||||
if (requires_value) {
|
||||
l_part += "=<value>";
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
#include <stdexcept>
|
||||
|
||||
namespace argument_parser::conventions::implementations {
|
||||
windows_argument_convention::windows_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {}
|
||||
windows_argument_convention::windows_argument_convention(bool const accept_dash) : accept_dash_(accept_dash) {}
|
||||
|
||||
parsed_argument windows_argument_convention::get_argument(std::string const &raw) const {
|
||||
if (raw.empty()) {
|
||||
return {argument_type::ERROR, "Empty argument token."};
|
||||
}
|
||||
const char c0 = raw[0];
|
||||
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
|
||||
if (!ok_prefix) {
|
||||
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
|
||||
return {argument_type::ERROR,
|
||||
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
||||
: "Windows-style expects options to start with '/'."};
|
||||
@@ -52,17 +51,17 @@ namespace argument_parser::conventions::implementations {
|
||||
|
||||
std::pair<std::string, std::string> 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 != "") {
|
||||
bool const requires_value) const {
|
||||
std::string s_part;
|
||||
if (short_arg != "-" && !short_arg.empty()) {
|
||||
s_part += short_prec() + short_arg;
|
||||
if (requires_value) {
|
||||
s_part += " <value>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string l_part = "";
|
||||
if (long_arg != "-" && long_arg != "") {
|
||||
std::string l_part;
|
||||
if (long_arg != "-" && !long_arg.empty()) {
|
||||
l_part += long_prec() + long_arg;
|
||||
if (requires_value) {
|
||||
l_part += " <value>";
|
||||
@@ -79,15 +78,14 @@ namespace argument_parser::conventions::implementations {
|
||||
} // namespace argument_parser::conventions::implementations
|
||||
|
||||
namespace argument_parser::conventions::implementations {
|
||||
windows_kv_argument_convention::windows_kv_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {}
|
||||
windows_kv_argument_convention::windows_kv_argument_convention(bool const accept_dash) : accept_dash_(accept_dash) {}
|
||||
|
||||
parsed_argument windows_kv_argument_convention::get_argument(std::string const &raw) const {
|
||||
if (raw.empty()) {
|
||||
return {argument_type::ERROR, "Empty argument token."};
|
||||
}
|
||||
const char c0 = raw[0];
|
||||
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
|
||||
if (!ok_prefix) {
|
||||
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
|
||||
return {argument_type::ERROR,
|
||||
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
||||
: "Windows-style expects options to start with '/'."};
|
||||
@@ -131,17 +129,17 @@ namespace argument_parser::conventions::implementations {
|
||||
|
||||
std::pair<std::string, std::string> windows_kv_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 != "") {
|
||||
bool const requires_value) const {
|
||||
std::string s_part;
|
||||
if (short_arg != "-" && !short_arg.empty()) {
|
||||
s_part += short_prec() + short_arg;
|
||||
if (requires_value) {
|
||||
s_part += "=<value>, " + short_prec() + short_arg + ":<value>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string l_part = "";
|
||||
if (long_arg != "-" && long_arg != "") {
|
||||
std::string l_part;
|
||||
if (long_arg != "-" && !long_arg.empty()) {
|
||||
l_part += long_prec() + long_arg;
|
||||
if (requires_value) {
|
||||
l_part += "=<value>, " + long_prec() + long_arg + ":<value>";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "argument_parser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
@@ -13,7 +14,7 @@
|
||||
|
||||
class deferred_exec {
|
||||
public:
|
||||
deferred_exec(std::function<void()> const &func) : func(func) {}
|
||||
explicit deferred_exec(std::function<void()> const &func) : func(func) {}
|
||||
~deferred_exec() {
|
||||
func();
|
||||
}
|
||||
@@ -28,12 +29,12 @@ bool contains(std::unordered_map<std::string, int> const &map, std::string const
|
||||
|
||||
namespace argument_parser {
|
||||
argument::argument()
|
||||
: id(0), name(), action(std::make_unique<non_parametered_action>([]() {})), required(false), invoked(false) {}
|
||||
: id(0), action(std::make_unique<non_parametered_action>([] {})), required(false), invoked(false) {}
|
||||
|
||||
argument::argument(const argument &other)
|
||||
: id(other.id), name(other.name), action(other.action->clone()), required(other.required),
|
||||
invoked(other.invoked), help_text(other.help_text), positional(other.positional),
|
||||
position_index(other.position_index) {}
|
||||
positional_accumulator(other.positional_accumulator), position_index(other.position_index) {}
|
||||
|
||||
argument &argument::operator=(const argument &other) {
|
||||
if (this != &other) {
|
||||
@@ -44,6 +45,7 @@ namespace argument_parser {
|
||||
invoked = other.invoked;
|
||||
help_text = other.help_text;
|
||||
positional = other.positional;
|
||||
positional_accumulator = other.positional_accumulator;
|
||||
position_index = other.position_index;
|
||||
}
|
||||
return *this;
|
||||
@@ -69,11 +71,11 @@ namespace argument_parser {
|
||||
return help_text;
|
||||
}
|
||||
|
||||
void argument::set_required(bool val) {
|
||||
void argument::set_required(const bool val) {
|
||||
required = val;
|
||||
}
|
||||
|
||||
void argument::set_invoked(bool val) {
|
||||
void argument::set_invoked(const bool val) {
|
||||
invoked = val;
|
||||
}
|
||||
|
||||
@@ -85,20 +87,36 @@ namespace argument_parser {
|
||||
return positional;
|
||||
}
|
||||
|
||||
bool argument::is_positional_accumulator() const {
|
||||
return positional_accumulator;
|
||||
}
|
||||
|
||||
std::optional<int> argument::get_position_index() const {
|
||||
return position_index;
|
||||
}
|
||||
|
||||
void argument::set_positional(bool val) {
|
||||
void argument::set_positional(const bool val) {
|
||||
positional = val;
|
||||
}
|
||||
|
||||
void argument::set_position_index(std::optional<int> idx) {
|
||||
void argument::set_positional_accumulator(const bool val) {
|
||||
positional_accumulator = val;
|
||||
}
|
||||
|
||||
void argument::set_position_index(const std::optional<int> idx) {
|
||||
position_index = idx;
|
||||
}
|
||||
|
||||
void base_parser::on_complete(std::function<void(base_parser const &)> const &handler) {
|
||||
on_complete_events.emplace_back(handler);
|
||||
void base_parser::on_complete(std::function<void(base_parser const &)> const &action) {
|
||||
on_complete_events.emplace_back(action);
|
||||
}
|
||||
|
||||
void base_parser::on_complete(std::function<void(base_parser const &)> const &handler, const bool to_start) {
|
||||
if (to_start) {
|
||||
on_complete_events.emplace_front(handler);
|
||||
} else {
|
||||
on_complete_events.emplace_back(handler);
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
@@ -112,8 +130,7 @@ namespace argument_parser {
|
||||
auto name_it = reverse_positional_names.find(pos_id);
|
||||
if (name_it == reverse_positional_names.end())
|
||||
continue;
|
||||
auto const &arg = argument_map.at(pos_id);
|
||||
if (arg.is_required()) {
|
||||
if (auto const &arg = argument_map.at(pos_id); arg.is_required()) {
|
||||
ss << " <" << name_it->second << ">";
|
||||
} else {
|
||||
ss << " [" << name_it->second << "]";
|
||||
@@ -143,8 +160,8 @@ namespace argument_parser {
|
||||
std::unordered_set<std::string> hasOnce;
|
||||
for (auto const &convention : convention_types) {
|
||||
auto generatedParts = convention->make_help_text(short_arg, long_arg, arg.expects_parameter());
|
||||
std::string combined = generatedParts.first + "|" + generatedParts.second;
|
||||
if (hasOnce.find(combined) == hasOnce.end()) {
|
||||
if (std::string combined = generatedParts.first + "|" + generatedParts.second;
|
||||
hasOnce.find(combined) == hasOnce.end()) {
|
||||
parts.push_back(generatedParts);
|
||||
hasOnce.insert(combined);
|
||||
|
||||
@@ -155,24 +172,24 @@ namespace argument_parser {
|
||||
max_long_len = generatedParts.second.length();
|
||||
}
|
||||
} else {
|
||||
parts.push_back({"", ""}); // trigger empty space in the help text
|
||||
parts.emplace_back("", ""); // trigger empty space in the help text
|
||||
}
|
||||
}
|
||||
help_lines.push_back({parts, arg.help_text});
|
||||
}
|
||||
|
||||
if (!help_lines.empty()) {
|
||||
for (auto const &line : help_lines) {
|
||||
for (const auto &[convention_parts, desc] : help_lines) {
|
||||
ss << "\t";
|
||||
for (size_t i = 0; i < line.convention_parts.size(); ++i) {
|
||||
auto const &parts = line.convention_parts[i];
|
||||
for (size_t i = 0; i < convention_parts.size(); ++i) {
|
||||
const auto &[fst, snd] = convention_parts[i];
|
||||
if (i > 0) {
|
||||
ss << " ";
|
||||
}
|
||||
ss << std::left << std::setw(static_cast<int>(max_short_len)) << parts.first << " "
|
||||
<< std::setw(static_cast<int>(max_long_len)) << parts.second;
|
||||
ss << std::left << std::setw(static_cast<int>(max_short_len)) << fst << " "
|
||||
<< std::setw(static_cast<int>(max_long_len)) << snd;
|
||||
}
|
||||
ss << "\t" << line.desc << "\n";
|
||||
ss << "\t" << desc << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,10 +199,8 @@ namespace argument_parser {
|
||||
for (auto const &pos_id : positional_arguments) {
|
||||
if (pos_id == -1)
|
||||
continue;
|
||||
auto name_it = reverse_positional_names.find(pos_id);
|
||||
if (name_it != reverse_positional_names.end()) {
|
||||
size_t display_len = name_it->second.length() + 2; // for < >
|
||||
if (display_len > max_pos_name_len)
|
||||
if (auto name_it = reverse_positional_names.find(pos_id); name_it != reverse_positional_names.end()) {
|
||||
if (size_t display_len = name_it->second.length() + 2; display_len > max_pos_name_len)
|
||||
max_pos_name_len = display_len;
|
||||
}
|
||||
}
|
||||
@@ -208,37 +223,32 @@ namespace argument_parser {
|
||||
|
||||
argument &base_parser::get_argument(conventions::parsed_argument const &arg) {
|
||||
if (arg.first == conventions::argument_type::LONG) {
|
||||
auto long_pos = long_arguments.find(arg.second);
|
||||
if (long_pos != long_arguments.end())
|
||||
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
|
||||
return argument_map.at(long_pos->second);
|
||||
} else if (arg.first == conventions::argument_type::SHORT) {
|
||||
auto short_pos = short_arguments.find(arg.second);
|
||||
if (short_pos != short_arguments.end())
|
||||
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
|
||||
return argument_map.at(short_pos->second);
|
||||
} else if (arg.first == conventions::argument_type::INTERCHANGABLE) {
|
||||
auto long_pos = long_arguments.find(arg.second);
|
||||
if (long_pos != long_arguments.end())
|
||||
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
|
||||
return argument_map.at(long_pos->second);
|
||||
auto short_pos = short_arguments.find(arg.second);
|
||||
if (short_pos != short_arguments.end())
|
||||
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
|
||||
return argument_map.at(short_pos->second);
|
||||
}
|
||||
throw std::runtime_error("Unknown argument: " + arg.second);
|
||||
}
|
||||
|
||||
void base_parser::enforce_creation_thread() {
|
||||
void base_parser::enforce_creation_thread() const {
|
||||
if (std::this_thread::get_id() != this->creation_thread_id.load()) {
|
||||
throw std::runtime_error("handle_arguments must be called from the main thread");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
bool
|
||||
base_parser::test_conventions(const std::initializer_list<conventions::convention const *const> convention_types,
|
||||
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help,
|
||||
std::vector<std::string>::iterator &it, std::stringstream &error_stream) {
|
||||
|
||||
std::string current_argument = *it;
|
||||
const std::string current_argument = *it;
|
||||
|
||||
for (auto const &convention_type : convention_types) {
|
||||
auto extracted = convention_type->get_argument(current_argument);
|
||||
@@ -256,16 +266,17 @@ namespace argument_parser {
|
||||
return true;
|
||||
}
|
||||
|
||||
found_arguments.emplace_back(extracted.second, corresponding_argument);
|
||||
found_argument found{extracted.second, corresponding_argument};
|
||||
|
||||
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);
|
||||
}
|
||||
values_for_arguments[extracted.second] =
|
||||
found.value =
|
||||
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
||||
}
|
||||
|
||||
found_arguments.emplace_back(std::move(found));
|
||||
return true;
|
||||
} catch (const std::runtime_error &e) {
|
||||
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what() << "\n";
|
||||
@@ -275,10 +286,9 @@ namespace argument_parser {
|
||||
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) {
|
||||
void
|
||||
base_parser::extract_arguments(const std::initializer_list<conventions::convention const *const> convention_types,
|
||||
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help) {
|
||||
|
||||
size_t next_positional_index = 0;
|
||||
bool force_positional = false;
|
||||
@@ -290,29 +300,30 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
if (force_positional) {
|
||||
if (next_positional_index >= positional_arguments.size()) {
|
||||
auto slot = next_positional_slot(next_positional_index);
|
||||
if (!slot.has_value()) {
|
||||
throw std::runtime_error("Unexpected positional argument: \"" + *it + "\"");
|
||||
}
|
||||
int arg_id = positional_arguments[next_positional_index];
|
||||
int arg_id = positional_arguments[*slot];
|
||||
argument &pos_arg = argument_map.at(arg_id);
|
||||
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
||||
found_arguments.emplace_back(pos_name, pos_arg);
|
||||
values_for_arguments[pos_name] = *it;
|
||||
next_positional_index++;
|
||||
found_arguments.push_back({pos_name, pos_arg, *it});
|
||||
if (!pos_arg.is_positional_accumulator()) {
|
||||
next_positional_index = *slot + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream error_stream;
|
||||
|
||||
if (!test_conventions(convention_types, values_for_arguments, found_arguments, found_help, it,
|
||||
error_stream)) {
|
||||
if (next_positional_index < positional_arguments.size()) {
|
||||
int arg_id = positional_arguments[next_positional_index];
|
||||
if (std::stringstream error_stream;
|
||||
!test_conventions(convention_types, found_arguments, found_help, it, error_stream)) {
|
||||
if (auto slot = next_positional_slot(next_positional_index); slot.has_value()) {
|
||||
int arg_id = positional_arguments[*slot];
|
||||
argument &pos_arg = argument_map.at(arg_id);
|
||||
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
||||
found_arguments.emplace_back(pos_name, pos_arg);
|
||||
values_for_arguments[pos_name] = *it;
|
||||
next_positional_index++;
|
||||
found_arguments.push_back({pos_name, pos_arg, *it});
|
||||
if (!pos_arg.is_positional_accumulator()) {
|
||||
next_positional_index = *slot + 1;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("All trials for argument: \n\t\"" + *it + "\"\n failed with: \n" +
|
||||
error_stream.str());
|
||||
@@ -322,7 +333,7 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
std::string replace_var(std::string text, const std::string &var_name, const std::string &value) {
|
||||
std::string placeholder = "${" + var_name + "}";
|
||||
const std::string placeholder = "${" + var_name + "}";
|
||||
size_t pos = text.find(placeholder);
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
@@ -332,8 +343,7 @@ namespace argument_parser {
|
||||
return text;
|
||||
}
|
||||
|
||||
void base_parser::invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
|
||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
||||
void base_parser::invoke_arguments(std::vector<found_argument> &found_arguments,
|
||||
std::optional<argument> const &found_help) {
|
||||
|
||||
if (found_help) {
|
||||
@@ -342,15 +352,15 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
std::stringstream error_stream;
|
||||
for (auto &[key, value] : found_arguments) {
|
||||
for (auto &[key, arg, value] : found_arguments) {
|
||||
try {
|
||||
if (value.expects_parameter()) {
|
||||
value.action->invoke_with_parameter(values_for_arguments.at(key));
|
||||
if (arg.expects_parameter()) {
|
||||
arg.action->invoke_with_parameter(value.value());
|
||||
} else {
|
||||
value.action->invoke();
|
||||
arg.action->invoke();
|
||||
}
|
||||
value.set_invoked(true);
|
||||
argument_map.at(value.id).set_invoked(true);
|
||||
arg.set_invoked(true);
|
||||
argument_map.at(arg.id).set_invoked(true);
|
||||
} catch (const std::runtime_error &e) {
|
||||
std::string err{e.what()};
|
||||
err = replace_var(err, "KEY", "for " + key);
|
||||
@@ -358,43 +368,42 @@ namespace argument_parser {
|
||||
}
|
||||
}
|
||||
|
||||
std::string error_message = error_stream.str();
|
||||
if (!error_message.empty()) {
|
||||
if (const std::string error_message = error_stream.str(); !error_message.empty()) {
|
||||
throw std::runtime_error(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
void base_parser::handle_arguments(std::initializer_list<conventions::convention const *const> convention_types) {
|
||||
void
|
||||
base_parser::handle_arguments(const std::initializer_list<conventions::convention const *const> convention_types) {
|
||||
enforce_creation_thread();
|
||||
|
||||
deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); });
|
||||
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::vector<found_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);
|
||||
extract_arguments(convention_types, found_arguments, found_help);
|
||||
invoke_arguments(found_arguments, found_help);
|
||||
check_for_required_arguments(convention_types);
|
||||
fire_on_complete_events();
|
||||
}
|
||||
|
||||
void base_parser::display_help(std::initializer_list<conventions::convention const *const> convention_types) const {
|
||||
void base_parser::display_help(
|
||||
const std::initializer_list<conventions::convention const *const> convention_types) const {
|
||||
std::cout << build_help_text(convention_types);
|
||||
}
|
||||
|
||||
std::optional<int> base_parser::find_argument_id(std::string const &arg) const {
|
||||
auto long_pos = long_arguments.find(arg);
|
||||
auto short_post = short_arguments.find(arg);
|
||||
const auto long_pos = long_arguments.find(arg);
|
||||
const auto short_post = short_arguments.find(arg);
|
||||
|
||||
if (long_pos != long_arguments.end())
|
||||
return long_pos->second;
|
||||
if (short_post != short_arguments.end())
|
||||
return short_post->second;
|
||||
|
||||
auto pos_it = positional_name_map.find(arg);
|
||||
if (pos_it != positional_name_map.end())
|
||||
if (const auto pos_it = positional_name_map.find(arg); pos_it != positional_name_map.end())
|
||||
return pos_it->second;
|
||||
|
||||
return std::nullopt;
|
||||
@@ -406,12 +415,12 @@ namespace argument_parser {
|
||||
}
|
||||
}
|
||||
|
||||
void base_parser::set_argument_status(bool is_required, std::string const &help_text, argument &arg) {
|
||||
void base_parser::set_argument_status(const bool is_required, std::string const &help_text, argument &arg) {
|
||||
arg.set_required(is_required);
|
||||
arg.set_help_text(help_text);
|
||||
}
|
||||
|
||||
void base_parser::place_argument(int id, argument const &arg, std::string const &short_arg,
|
||||
void base_parser::place_argument(int const id, argument const &arg, std::string const &short_arg,
|
||||
std::string const &long_arg) {
|
||||
argument_map[id] = arg;
|
||||
if (short_arg != "-") {
|
||||
@@ -430,19 +439,57 @@ namespace argument_parser {
|
||||
}
|
||||
}
|
||||
|
||||
void base_parser::place_positional_argument(int id, argument const &arg, std::string const &name,
|
||||
std::optional<int> position) {
|
||||
void base_parser::assert_can_place_positional(const int id, const std::optional<int> position,
|
||||
const bool accumulator) const {
|
||||
const auto existing_accumulator =
|
||||
std::find_if(positional_arguments.begin(), positional_arguments.end(), [this](int const arg_id) {
|
||||
if (arg_id == -1) {
|
||||
return false;
|
||||
}
|
||||
return argument_map.at(arg_id).is_positional_accumulator();
|
||||
});
|
||||
|
||||
if (accumulator && existing_accumulator != positional_arguments.end()) {
|
||||
throw std::runtime_error("Only one positional accumulator is allowed.");
|
||||
}
|
||||
|
||||
if (!accumulator && existing_accumulator != positional_arguments.end()) {
|
||||
const auto accumulator_slot =
|
||||
static_cast<size_t>(std::distance(positional_arguments.begin(), existing_accumulator));
|
||||
if (!position.has_value() || static_cast<size_t>(position.value()) >= accumulator_slot) {
|
||||
throw std::runtime_error("Positional accumulator must be the last positional argument.");
|
||||
}
|
||||
}
|
||||
|
||||
if (accumulator && position.has_value()) {
|
||||
const auto idx = static_cast<size_t>(position.value());
|
||||
for (size_t i = idx + 1; i < positional_arguments.size(); ++i) {
|
||||
if (positional_arguments[i] != -1 && positional_arguments[i] != id) {
|
||||
throw std::runtime_error("Positional accumulator must be the last positional argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void base_parser::place_positional_argument(int const id, argument const &arg, std::string const &name,
|
||||
const std::optional<int> position, const bool accumulator) {
|
||||
if (position.has_value() && position.value() < 0) {
|
||||
throw std::runtime_error("Positional argument position cannot be negative.");
|
||||
}
|
||||
assert_can_place_positional(id, position, accumulator);
|
||||
|
||||
argument_map[id] = arg;
|
||||
positional_name_map[name] = id;
|
||||
reverse_positional_names[id] = name;
|
||||
|
||||
if (position.has_value()) {
|
||||
auto idx = static_cast<size_t>(position.value());
|
||||
const auto idx = static_cast<size_t>(position.value());
|
||||
if (idx > positional_arguments.size()) {
|
||||
positional_arguments.resize(idx + 1, -1);
|
||||
}
|
||||
if (idx < positional_arguments.size() && positional_arguments[idx] != -1) {
|
||||
throw std::runtime_error("Position " + std::to_string(idx) + " is already occupied!");
|
||||
positional_arguments.insert(positional_arguments.begin() + static_cast<std::ptrdiff_t>(idx), id);
|
||||
return;
|
||||
}
|
||||
if (idx == positional_arguments.size()) {
|
||||
positional_arguments.push_back(id);
|
||||
@@ -450,10 +497,24 @@ namespace argument_parser {
|
||||
positional_arguments[idx] = id;
|
||||
}
|
||||
} else {
|
||||
positional_arguments.push_back(id);
|
||||
if (const auto empty_slot = std::find(positional_arguments.begin(), positional_arguments.end(), -1);
|
||||
empty_slot != positional_arguments.end()) {
|
||||
*empty_slot = id;
|
||||
} else {
|
||||
positional_arguments.push_back(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<size_t> base_parser::next_positional_slot(size_t const start) const {
|
||||
for (size_t i = start; i < positional_arguments.size(); ++i) {
|
||||
if (positional_arguments[i] != -1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string get_one_name(std::string const &short_name, std::string const &long_name) {
|
||||
std::string res{};
|
||||
if (short_name != "-") {
|
||||
@@ -500,15 +561,15 @@ namespace argument_parser {
|
||||
} else {
|
||||
std::cerr << "\t" << get_one_name(s, l) << ": must be provided as one of [";
|
||||
for (auto it = convention_types.begin(); it != convention_types.end(); ++it) {
|
||||
auto generatedParts = (*it)->make_help_text(s, l, p);
|
||||
std::string help_str = generatedParts.first;
|
||||
if (!generatedParts.first.empty() && !generatedParts.second.empty()) {
|
||||
auto [short_part, long_part] = (*it)->make_help_text(s, l, p);
|
||||
std::string help_str = short_part;
|
||||
if (!short_part.empty() && !long_part.empty()) {
|
||||
help_str += " ";
|
||||
}
|
||||
help_str += generatedParts.second;
|
||||
help_str += long_part;
|
||||
|
||||
size_t last_not_space = help_str.find_last_not_of(" \t");
|
||||
if (last_not_space != std::string::npos) {
|
||||
if (size_t last_not_space = help_str.find_last_not_of(" \t");
|
||||
last_not_space != std::string::npos) {
|
||||
help_str.erase(last_not_space + 1);
|
||||
}
|
||||
std::cerr << help_str;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
|
||||
: fake_parser(program_name, std::vector<std::string>(arguments)) {}
|
||||
: fake_parser(program_name, std::vector(arguments)) {}
|
||||
|
||||
void fake_parser::set_program_name(std::string const &program_name) {
|
||||
this->program_name = program_name;
|
||||
@@ -24,22 +24,22 @@ namespace argument_parser {
|
||||
|
||||
namespace v2 {
|
||||
fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) {
|
||||
set_program_name(program_name);
|
||||
base_parser::set_program_name(std::move(program_name));
|
||||
ref_parsed_args() = arguments;
|
||||
prepare_help_flag(false);
|
||||
}
|
||||
|
||||
fake_parser::fake_parser(std::string const &program_name, std::vector<std::string> &&arguments) {
|
||||
set_program_name(program_name);
|
||||
fake_parser::fake_parser(std::string program_name, std::vector<std::string> &&arguments) {
|
||||
base_parser::set_program_name(std::move(program_name));
|
||||
ref_parsed_args() = std::move(arguments);
|
||||
prepare_help_flag(false);
|
||||
}
|
||||
|
||||
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
|
||||
: fake_parser(program_name, std::vector<std::string>(arguments)) {}
|
||||
fake_parser::fake_parser(std::string program_name, std::initializer_list<std::string> const &arguments)
|
||||
: fake_parser(std::move(program_name), std::vector(arguments)) {}
|
||||
|
||||
void fake_parser::set_program_name(std::string const &program_name) {
|
||||
set_program_name(program_name);
|
||||
base_parser::set_program_name(program_name);
|
||||
}
|
||||
|
||||
void fake_parser::set_parsed_arguments(std::vector<std::string> const &parsed_arguments) {
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
#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) { \
|
||||
const int (argc_name) = *_NSGetArgc(); \
|
||||
if (char **(argv_name) = *_NSGetArgv(); (argc_name) > 0 && (argv_name) != nullptr && (argv_name)[0] != nullptr) { \
|
||||
do { \
|
||||
before_for; \
|
||||
(before_for); \
|
||||
} while (false); \
|
||||
for (int i = 1; i < argc_name; ++i) { \
|
||||
for_body \
|
||||
for (int i = 1; i < (argc_name); ++i) { \
|
||||
{for_body} \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
@@ -26,7 +26,7 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
namespace v2 {
|
||||
macos_parser::macos_parser(bool should_exit) {
|
||||
macos_parser::macos_parser(const bool should_exit) {
|
||||
MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), {
|
||||
if (argv[i] != nullptr)
|
||||
ref_parsed_args().emplace_back(argv[i]);
|
||||
|
||||
Reference in New Issue
Block a user