mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-05-28 20:08:10 +00:00
feat: introduce reference return, accumulators. chore: lint
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
#include <parser_v2.hpp>
|
#include <parser_v2.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <traits.hpp>
|
#include <traits.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <windows_argument_convention.hpp>
|
||||||
|
|
||||||
using argument = argument_parser::builder::argument<>;
|
using argument = argument_parser::builder::argument<>;
|
||||||
|
|
||||||
@@ -18,9 +20,7 @@ auto echo(std::string const &s) -> void {
|
|||||||
using namespace argument_parser::parsing_traits;
|
using namespace argument_parser::parsing_traits;
|
||||||
|
|
||||||
constexpr hint_type vector_purpose_hint = "vector of ";
|
constexpr hint_type vector_purpose_hint = "vector of ";
|
||||||
|
template <typename T> struct parser_trait<std::vector<T>> {
|
||||||
template <typename T> class parser_trait<std::vector<T>> {
|
|
||||||
public:
|
|
||||||
static std::vector<T> parse(std::string const &s) {
|
static std::vector<T> parse(std::string const &s) {
|
||||||
std::vector<T> result;
|
std::vector<T> result;
|
||||||
std::stringstream ss(s);
|
std::stringstream ss(s);
|
||||||
@@ -96,9 +96,61 @@ auto main() -> int {
|
|||||||
})
|
})
|
||||||
.build(parser);
|
.build(parser);
|
||||||
|
|
||||||
parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention});
|
auto accumulate_vec =
|
||||||
|
argument::start().long_argument("vecstr1").short_argument("vs1").accumulate<int>().build_and_get(parser);
|
||||||
|
|
||||||
std::cout << "captured value: " << captured_value << '\n';
|
parser.add_argument<std::vector<int>>({
|
||||||
|
{LongArgument, "accumulate"},
|
||||||
|
{HelpText, "accumulates given ints into the vector (flag ver)"},
|
||||||
|
{Accumulate, true},
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<int> captured_vec;
|
||||||
|
parser.add_argument<std::vector<int>>({
|
||||||
|
{LongArgument, "accumulate2"},
|
||||||
|
{HelpText, "accumulates given ints into the vector (ref ver)"},
|
||||||
|
{Accumulate, &captured_vec},
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<int> captured_vec2;
|
||||||
|
parser.add_argument<std::vector<int>>({
|
||||||
|
{LongArgument, "accumulate3"},
|
||||||
|
{HelpText, "accumulates given ints into the vector (ref ver)"},
|
||||||
|
{Accumulate, true},
|
||||||
|
{Reference, &captured_vec2},
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.on_complete([](argument_parser::base_parser const &p) {
|
||||||
|
if (const auto value = p.get_optional<std::vector<int>>("accumulate"); value.has_value()) {
|
||||||
|
std::cout << "accumulate: ";
|
||||||
|
for (auto const &str : *value) {
|
||||||
|
std::cout << str << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention,
|
||||||
|
&argument_parser::conventions::windows_argument_convention});
|
||||||
|
if (!captured_vec.empty()) {
|
||||||
|
std::cout << "accumulate2: ";
|
||||||
|
for (auto const &str : captured_vec) {
|
||||||
|
std::cout << str << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!captured_vec2.empty()) {
|
||||||
|
std::cout << "accumulate3: ";
|
||||||
|
for (auto const &str : captured_vec2) {
|
||||||
|
std::cout << str << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accumulate_vec) {
|
||||||
|
std::cout << "accumulate_vec: ";
|
||||||
|
for (auto const &str : *accumulate_vec) {
|
||||||
|
std::cout << str << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ namespace argument_parser::conventions {
|
|||||||
|
|
||||||
class base_convention {
|
class base_convention {
|
||||||
public:
|
public:
|
||||||
virtual std::string extract_value(std::string const &) const = 0;
|
[[nodiscard]] virtual std::string extract_value(std::string const &) const = 0;
|
||||||
virtual parsed_argument get_argument(std::string const &) const = 0;
|
[[nodiscard]] virtual parsed_argument get_argument(std::string const &) const = 0;
|
||||||
virtual bool requires_next_token() const = 0;
|
[[nodiscard]] virtual bool requires_next_token() const = 0;
|
||||||
virtual std::string name() const = 0;
|
[[nodiscard]] virtual std::string name() const = 0;
|
||||||
virtual std::string short_prec() const = 0;
|
[[nodiscard]] virtual std::string short_prec() const = 0;
|
||||||
virtual std::string long_prec() const = 0;
|
[[nodiscard]] virtual std::string long_prec() const = 0;
|
||||||
virtual std::pair<std::string, std::string>
|
[[nodiscard]] virtual std::pair<std::string, std::string>
|
||||||
make_help_text(std::string const &short_arg, std::string const &long_arg, bool requires_value) const = 0;
|
make_help_text(std::string const &short_arg, std::string const &long_arg, bool requires_value) const = 0;
|
||||||
virtual std::vector<convention_features> get_features() const = 0;
|
[[nodiscard]] virtual std::vector<convention_features> get_features() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
base_convention() = default;
|
base_convention() = default;
|
||||||
|
|||||||
@@ -8,15 +8,16 @@ namespace argument_parser::conventions::implementations {
|
|||||||
|
|
||||||
class gnu_argument_convention : public base_convention {
|
class gnu_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
parsed_argument get_argument(std::string const &raw) const override;
|
virtual ~gnu_argument_convention() = default;
|
||||||
std::string extract_value(std::string const & /*raw*/) const override;
|
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
|
||||||
bool requires_next_token() const override;
|
[[nodiscard]] std::string extract_value(std::string const & /*raw*/) const override;
|
||||||
std::string name() const override;
|
[[nodiscard]] bool requires_next_token() const override;
|
||||||
std::string short_prec() const override;
|
[[nodiscard]] std::string name() const override;
|
||||||
std::string long_prec() const override;
|
[[nodiscard]] std::string short_prec() const override;
|
||||||
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
|
[[nodiscard]] std::string long_prec() const override;
|
||||||
|
[[nodiscard]] std::pair<std::string, std::string> 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<convention_features> get_features() const override;
|
[[nodiscard]] std::vector<convention_features> get_features() const override;
|
||||||
|
|
||||||
static gnu_argument_convention instance;
|
static gnu_argument_convention instance;
|
||||||
|
|
||||||
@@ -26,15 +27,16 @@ namespace argument_parser::conventions::implementations {
|
|||||||
|
|
||||||
class gnu_equal_argument_convention : public base_convention {
|
class gnu_equal_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
parsed_argument get_argument(std::string const &raw) const override;
|
virtual ~gnu_equal_argument_convention() = default;
|
||||||
std::string extract_value(std::string const &raw) const override;
|
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
|
||||||
bool requires_next_token() const override;
|
[[nodiscard]] std::string extract_value(std::string const &raw) const override;
|
||||||
std::string name() const override;
|
[[nodiscard]] bool requires_next_token() const override;
|
||||||
std::string short_prec() const override;
|
[[nodiscard]] std::string name() const override;
|
||||||
std::string long_prec() const override;
|
[[nodiscard]] std::string short_prec() const override;
|
||||||
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
|
[[nodiscard]] std::string long_prec() const override;
|
||||||
|
[[nodiscard]] std::pair<std::string, std::string> 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<convention_features> get_features() const override;
|
[[nodiscard]] std::vector<convention_features> get_features() const override;
|
||||||
|
|
||||||
static gnu_equal_argument_convention instance;
|
static gnu_equal_argument_convention instance;
|
||||||
|
|
||||||
|
|||||||
@@ -5,22 +5,23 @@
|
|||||||
#define WINDOWS_ARGUMENT_CONVENTION_HPP
|
#define WINDOWS_ARGUMENT_CONVENTION_HPP
|
||||||
|
|
||||||
#ifndef ALLOW_DASH_FOR_WINDOWS
|
#ifndef ALLOW_DASH_FOR_WINDOWS
|
||||||
#define ALLOW_DASH_FOR_WINDOWS 1
|
#define ALLOW_DASH_FOR_WINDOWS true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace argument_parser::conventions::implementations {
|
namespace argument_parser::conventions::implementations {
|
||||||
class windows_argument_convention : public base_convention {
|
class windows_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
|
virtual ~windows_argument_convention() = default;
|
||||||
explicit windows_argument_convention(bool accept_dash = true);
|
explicit windows_argument_convention(bool accept_dash = true);
|
||||||
parsed_argument get_argument(std::string const &raw) const override;
|
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
|
||||||
std::string extract_value(std::string const & /*raw*/) const override;
|
[[nodiscard]] std::string extract_value(std::string const & /*raw*/) const override;
|
||||||
bool requires_next_token() const override;
|
[[nodiscard]] bool requires_next_token() const override;
|
||||||
std::string name() const override;
|
[[nodiscard]] std::string name() const override;
|
||||||
std::string short_prec() const override;
|
[[nodiscard]] std::string short_prec() const override;
|
||||||
std::string long_prec() const override;
|
[[nodiscard]] std::string long_prec() const override;
|
||||||
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
|
[[nodiscard]] std::pair<std::string, std::string> 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<convention_features> get_features() const override;
|
[[nodiscard]] std::vector<convention_features> get_features() const override;
|
||||||
|
|
||||||
static windows_argument_convention instance;
|
static windows_argument_convention instance;
|
||||||
|
|
||||||
@@ -30,16 +31,17 @@ namespace argument_parser::conventions::implementations {
|
|||||||
|
|
||||||
class windows_kv_argument_convention : public base_convention {
|
class windows_kv_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
|
virtual ~windows_kv_argument_convention() = default;
|
||||||
explicit windows_kv_argument_convention(bool accept_dash = true);
|
explicit windows_kv_argument_convention(bool accept_dash = true);
|
||||||
parsed_argument get_argument(std::string const &raw) const override;
|
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
|
||||||
std::string extract_value(std::string const &raw) const override;
|
[[nodiscard]] std::string extract_value(std::string const &raw) const override;
|
||||||
bool requires_next_token() const override;
|
[[nodiscard]] bool requires_next_token() const override;
|
||||||
std::string name() const override;
|
[[nodiscard]] std::string name() const override;
|
||||||
std::string short_prec() const override;
|
[[nodiscard]] std::string short_prec() const override;
|
||||||
std::string long_prec() const override;
|
[[nodiscard]] std::string long_prec() const override;
|
||||||
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
|
[[nodiscard]] std::pair<std::string, std::string> 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<convention_features> get_features() const override;
|
[[nodiscard]] std::vector<convention_features> get_features() const override;
|
||||||
|
|
||||||
static windows_kv_argument_convention instance;
|
static windows_kv_argument_convention instance;
|
||||||
|
|
||||||
@@ -48,9 +50,9 @@ namespace argument_parser::conventions::implementations {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline windows_argument_convention windows_argument_convention::instance =
|
inline windows_argument_convention windows_argument_convention::instance =
|
||||||
windows_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS));
|
windows_argument_convention(ALLOW_DASH_FOR_WINDOWS);
|
||||||
inline windows_kv_argument_convention windows_kv_argument_convention::instance =
|
inline windows_kv_argument_convention windows_kv_argument_convention::instance =
|
||||||
windows_kv_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS));
|
windows_kv_argument_convention(ALLOW_DASH_FOR_WINDOWS);
|
||||||
} // namespace argument_parser::conventions::implementations
|
} // namespace argument_parser::conventions::implementations
|
||||||
|
|
||||||
namespace argument_parser::conventions {
|
namespace argument_parser::conventions {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "argument_parser.hpp"
|
#include "argument_parser.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <parser_v2.hpp>
|
#include <parser_v2.hpp>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@@ -14,7 +15,15 @@ namespace argument_parser::builder {
|
|||||||
namespace builder_mask {
|
namespace builder_mask {
|
||||||
using v2_flag = argument_parser::v2::add_argument_flags;
|
using v2_flag = argument_parser::v2::add_argument_flags;
|
||||||
using mask_type = std::uint64_t;
|
using mask_type = std::uint64_t;
|
||||||
enum class value_mode { unresolved, store, flag, reference, nonparametered_action, parametered_action };
|
enum class value_mode {
|
||||||
|
unresolved,
|
||||||
|
store,
|
||||||
|
flag,
|
||||||
|
reference,
|
||||||
|
accumulate,
|
||||||
|
nonparametered_action,
|
||||||
|
parametered_action
|
||||||
|
};
|
||||||
|
|
||||||
enum class extra_capability : unsigned { Store = static_cast<unsigned>(v2_flag::Reference) + 1, Flag };
|
enum class extra_capability : unsigned { Store = static_cast<unsigned>(v2_flag::Reference) + 1, Flag };
|
||||||
|
|
||||||
@@ -34,12 +43,13 @@ namespace argument_parser::builder {
|
|||||||
constexpr mask_type action = bit(v2_flag::Action);
|
constexpr mask_type action = bit(v2_flag::Action);
|
||||||
constexpr mask_type required = bit(v2_flag::Required);
|
constexpr mask_type required = bit(v2_flag::Required);
|
||||||
constexpr mask_type reference = bit(v2_flag::Reference);
|
constexpr mask_type reference = bit(v2_flag::Reference);
|
||||||
|
constexpr mask_type accumulate = bit(v2_flag::Accumulate);
|
||||||
constexpr mask_type store = bit(extra_capability::Store);
|
constexpr mask_type store = bit(extra_capability::Store);
|
||||||
constexpr mask_type flag = bit(extra_capability::Flag);
|
constexpr mask_type flag = bit(extra_capability::Flag);
|
||||||
|
|
||||||
constexpr mask_type value_mode_group = action | reference | store | flag;
|
constexpr mask_type value_mode_group = action | reference | accumulate | store | flag;
|
||||||
constexpr mask_type initial =
|
constexpr mask_type initial = short_argument | long_argument | positional | help_text | action | required |
|
||||||
short_argument | long_argument | positional | help_text | action | required | reference | store | flag;
|
reference | accumulate | store | flag;
|
||||||
|
|
||||||
constexpr auto has(mask_type mask, mask_type capability) -> bool {
|
constexpr auto has(mask_type mask, mask_type capability) -> bool {
|
||||||
return (mask & capability) == capability;
|
return (mask & capability) == capability;
|
||||||
@@ -62,6 +72,39 @@ namespace argument_parser::builder {
|
|||||||
}
|
}
|
||||||
} // namespace builder_mask
|
} // namespace builder_mask
|
||||||
|
|
||||||
|
template <typename store_type> class container {
|
||||||
|
public:
|
||||||
|
store_type get() const {
|
||||||
|
return m_container.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
store_type &operator*() {
|
||||||
|
return m_container.operator*();
|
||||||
|
}
|
||||||
|
|
||||||
|
store_type *operator->() {
|
||||||
|
return m_container.operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() {
|
||||||
|
return m_container.operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
container() = default;
|
||||||
|
explicit container(store_type *ptr) {
|
||||||
|
m_container = std::shared_ptr<store_type>(ptr, [](store_type *) {
|
||||||
|
/* noop. we don't own this reference, so it is not ours to free, but still use std::shared_ptr to manage
|
||||||
|
* it. */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void set_container(store_type const &container) {
|
||||||
|
m_container = std::make_shared<store_type>(container);
|
||||||
|
}
|
||||||
|
std::shared_ptr<store_type> m_container;
|
||||||
|
template <builder_mask::mask_type mask, typename __store_type> friend class argument;
|
||||||
|
};
|
||||||
|
|
||||||
template <builder_mask::mask_type mask = builder_mask::initial, typename store_type = non_type> class argument {
|
template <builder_mask::mask_type mask = builder_mask::initial, typename store_type = non_type> class argument {
|
||||||
public:
|
public:
|
||||||
using mask_type = builder_mask::mask_type;
|
using mask_type = builder_mask::mask_type;
|
||||||
@@ -169,6 +212,34 @@ namespace argument_parser::builder {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T = std::string, mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::accumulate), int> = 0>
|
||||||
|
auto accumulate() const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), std::vector<T>> {
|
||||||
|
using vector_type = std::vector<T>;
|
||||||
|
using next_argument =
|
||||||
|
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), vector_type>;
|
||||||
|
|
||||||
|
next_argument next{*this};
|
||||||
|
next.m_value_mode = value_mode::accumulate;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::accumulate), int> = 0, typename T>
|
||||||
|
auto accumulate(T &value) const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
|
||||||
|
static_assert(argument_parser::v2::deducers::is_vector_v<T>,
|
||||||
|
"accumulate(target) requires a std::vector target.");
|
||||||
|
|
||||||
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
||||||
|
|
||||||
|
next_argument next{*this};
|
||||||
|
next.m_reference = std::addressof(value);
|
||||||
|
next.m_value_mode = value_mode::accumulate;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
template <mask_type current_mask = mask,
|
template <mask_type current_mask = mask,
|
||||||
std::enable_if_t<builder_mask::has(current_mask, builder_mask::flag), int> = 0>
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::flag), int> = 0>
|
||||||
auto flag() const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), bool> {
|
auto flag() const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), bool> {
|
||||||
@@ -246,6 +317,12 @@ namespace argument_parser::builder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case value_mode::accumulate:
|
||||||
|
if constexpr (!std::is_same_v<store_type, non_type>) {
|
||||||
|
build_accumulate(parser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case value_mode::parametered_action:
|
case value_mode::parametered_action:
|
||||||
if constexpr (!std::is_same_v<store_type, non_type>) {
|
if constexpr (!std::is_same_v<store_type, non_type>) {
|
||||||
build_parametered_action(parser);
|
build_parametered_action(parser);
|
||||||
@@ -264,6 +341,35 @@ namespace argument_parser::builder {
|
|||||||
throw std::logic_error("The builder reached build() without a supported terminal value mode.");
|
throw std::logic_error("The builder reached build() without a supported terminal value mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <mask_type current_mask = mask, std::enable_if_t<builder_mask::is_buildable(current_mask), int> = 0>
|
||||||
|
auto build_and_get(argument_parser::v2::base_parser &parser) const -> container<store_type> {
|
||||||
|
assert_has_identifier();
|
||||||
|
switch (m_value_mode) {
|
||||||
|
case value_mode::store:
|
||||||
|
case value_mode::flag:
|
||||||
|
case value_mode::unresolved:
|
||||||
|
case value_mode::accumulate:
|
||||||
|
build(parser);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::logic_error("The builder reached build() without a supported terminal value mode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_value_mode == value_mode::accumulate && m_reference != nullptr) {
|
||||||
|
return container(m_reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lk = lookup_key();
|
||||||
|
container<store_type> container;
|
||||||
|
parser.on_complete([lk, &container](base_parser const &p) {
|
||||||
|
auto value = p.get_optional<store_type>(lk);
|
||||||
|
if (value.has_value()) {
|
||||||
|
container.set_container(*value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
argument() = default;
|
argument() = default;
|
||||||
|
|
||||||
@@ -276,9 +382,9 @@ namespace argument_parser::builder {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using typed_map =
|
using typed_map =
|
||||||
std::unordered_map<v2_flag, typename argument_parser::v2::base_parser::template typed_flag_value<T>>;
|
std::unordered_map<v2_flag, v2::base_parser::typed_flag_value<T>>;
|
||||||
|
|
||||||
using non_typed_map = std::unordered_map<v2_flag, argument_parser::v2::base_parser::non_typed_flag_value>;
|
using non_typed_map = std::unordered_map<v2_flag, v2::base_parser::non_typed_flag_value>;
|
||||||
|
|
||||||
auto is_positional() const -> bool {
|
auto is_positional() const -> bool {
|
||||||
return !m_positional_name.empty();
|
return !m_positional_name.empty();
|
||||||
@@ -359,7 +465,6 @@ namespace argument_parser::builder {
|
|||||||
auto build_reference(argument_parser::v2::base_parser &parser) const -> void {
|
auto build_reference(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto pairs = make_typed_pairs<store_type>();
|
auto pairs = make_typed_pairs<store_type>();
|
||||||
auto *target = m_reference;
|
auto *target = m_reference;
|
||||||
auto key = lookup_key();
|
|
||||||
|
|
||||||
if (target == nullptr) {
|
if (target == nullptr) {
|
||||||
throw std::logic_error("reference() was selected without a target.");
|
throw std::logic_error("reference() was selected without a target.");
|
||||||
@@ -369,6 +474,16 @@ namespace argument_parser::builder {
|
|||||||
parser.template add_argument<store_type>(pairs);
|
parser.template add_argument<store_type>(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto build_accumulate(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
|
auto pairs = make_typed_pairs<store_type>();
|
||||||
|
if (m_reference != nullptr) {
|
||||||
|
pairs[argument_parser::v2::flags::Accumulate] = m_reference;
|
||||||
|
} else {
|
||||||
|
pairs[argument_parser::v2::flags::Accumulate] = true;
|
||||||
|
}
|
||||||
|
parser.template add_argument<store_type>(pairs);
|
||||||
|
}
|
||||||
|
|
||||||
auto build_parametered_action(argument_parser::v2::base_parser &parser) const -> void {
|
auto build_parametered_action(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto const *typed_action =
|
auto const *typed_action =
|
||||||
dynamic_cast<argument_parser::parametered_action<store_type> const *>(m_action.get());
|
dynamic_cast<argument_parser::parametered_action<store_type> const *>(m_action.get());
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// ReSharper disable CppFunctionIsNotImplemented
|
||||||
|
// ReSharper disable All
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ARGUMENT_PARSER_HPP
|
#ifndef ARGUMENT_PARSER_HPP
|
||||||
#define ARGUMENT_PARSER_HPP
|
#define ARGUMENT_PARSER_HPP
|
||||||
@@ -64,9 +66,9 @@ namespace argument_parser {
|
|||||||
copyable_atomic &operator=(copyable_atomic &&other) noexcept = default;
|
copyable_atomic &operator=(copyable_atomic &&other) noexcept = default;
|
||||||
~copyable_atomic() = default;
|
~copyable_atomic() = default;
|
||||||
|
|
||||||
T operator=(T desired) noexcept {
|
copyable_atomic& operator=(T desired) noexcept {
|
||||||
store(desired);
|
store(desired);
|
||||||
return desired;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator T() const noexcept {
|
operator T() const noexcept {
|
||||||
@@ -79,7 +81,7 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
|
[[nodiscard]] T load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
|
||||||
return value ? value->load(order) : T{};
|
return value ? value->load(order) : T{};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ namespace argument_parser {
|
|||||||
T parsed_value = parsing_traits::parser_trait<T>::parse(param);
|
T parsed_value = parsing_traits::parser_trait<T>::parse(param);
|
||||||
parse_success = true;
|
parse_success = true;
|
||||||
invoke(parsed_value);
|
invoke(parsed_value);
|
||||||
} catch (const std::runtime_error &e) {
|
} catch (const std::runtime_error &_) {
|
||||||
if (!parse_success) {
|
if (!parse_success) {
|
||||||
auto [format_hint, purpose_hint] = get_trait_hints();
|
auto [format_hint, purpose_hint] = get_trait_hints();
|
||||||
if (purpose_hint.empty())
|
if (purpose_hint.empty())
|
||||||
@@ -203,6 +205,7 @@ namespace argument_parser {
|
|||||||
[[nodiscard]] bool expects_parameter() const;
|
[[nodiscard]] bool expects_parameter() const;
|
||||||
[[nodiscard]] std::string get_help_text() const;
|
[[nodiscard]] std::string get_help_text() const;
|
||||||
[[nodiscard]] bool is_positional() const;
|
[[nodiscard]] bool is_positional() const;
|
||||||
|
[[nodiscard]] bool is_positional_accumulator() const;
|
||||||
[[nodiscard]] std::optional<int> get_position_index() const;
|
[[nodiscard]] std::optional<int> get_position_index() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -210,6 +213,7 @@ namespace argument_parser {
|
|||||||
void set_invoked(bool val);
|
void set_invoked(bool val);
|
||||||
void set_help_text(std::string const &text);
|
void set_help_text(std::string const &text);
|
||||||
void set_positional(bool val);
|
void set_positional(bool val);
|
||||||
|
void set_positional_accumulator(bool val);
|
||||||
void set_position_index(std::optional<int> idx);
|
void set_position_index(std::optional<int> idx);
|
||||||
|
|
||||||
friend class base_parser;
|
friend class base_parser;
|
||||||
@@ -221,6 +225,7 @@ namespace argument_parser {
|
|||||||
bool invoked;
|
bool invoked;
|
||||||
std::string help_text;
|
std::string help_text;
|
||||||
bool positional = false;
|
bool positional = false;
|
||||||
|
bool positional_accumulator = false;
|
||||||
std::optional<int> position_index = std::nullopt;
|
std::optional<int> position_index = std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,6 +284,13 @@ namespace argument_parser {
|
|||||||
base_add_positional_argument<T>(name, help_text, required, position);
|
base_add_positional_argument<T>(name, help_text, required, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void add_positional_accumulator(std::string const &name, std::string const &help_text,
|
||||||
|
parametered_action<T> const &action, bool required,
|
||||||
|
std::optional<int> position = std::nullopt) {
|
||||||
|
base_add_positional_argument(name, help_text, action, required, position, true);
|
||||||
|
}
|
||||||
|
|
||||||
void on_complete(std::function<void(base_parser const &)> const &action);
|
void on_complete(std::function<void(base_parser const &)> const &action);
|
||||||
|
|
||||||
template <typename T> std::optional<T> get_optional(std::string const &arg) const {
|
template <typename T> std::optional<T> get_optional(std::string const &arg) const {
|
||||||
@@ -317,28 +329,39 @@ namespace argument_parser {
|
|||||||
return _current_conventions;
|
return _current_conventions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<int, std::any> &ref_stored_arguments() {
|
||||||
|
return stored_arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_complete(std::function<void(base_parser const &)> const &handler, bool to_start);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct found_argument {
|
||||||
|
std::string key;
|
||||||
|
argument arg;
|
||||||
|
std::optional<std::string> value = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
bool test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
|
bool test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
|
||||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
std::vector<found_argument> &found_arguments,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
|
||||||
std::optional<argument> &found_help, std::vector<std::string>::iterator &it,
|
std::optional<argument> &found_help, std::vector<std::string>::iterator &it,
|
||||||
std::stringstream &error_stream);
|
std::stringstream &error_stream);
|
||||||
void extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
|
void extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
|
||||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
std::vector<found_argument> &found_arguments,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
|
||||||
std::optional<argument> &found_help);
|
std::optional<argument> &found_help);
|
||||||
|
|
||||||
void invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
|
void invoke_arguments(std::vector<found_argument> &found_arguments,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
|
||||||
std::optional<argument> const &found_help);
|
std::optional<argument> const &found_help);
|
||||||
void enforce_creation_thread();
|
void enforce_creation_thread() const;
|
||||||
|
|
||||||
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;
|
||||||
void assert_positional_not_exist(std::string const &name) const;
|
void assert_positional_not_exist(std::string const &name) const;
|
||||||
|
void assert_can_place_positional(int id, std::optional<int> position, bool accumulator) 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);
|
||||||
void place_positional_argument(int id, argument const &arg, std::string const &name,
|
void place_positional_argument(int id, argument const &arg, std::string const &name,
|
||||||
std::optional<int> position);
|
std::optional<int> position, bool accumulator = false);
|
||||||
|
[[nodiscard]] std::optional<size_t> next_positional_slot(size_t start) const;
|
||||||
|
|
||||||
template <typename ActionType>
|
template <typename ActionType>
|
||||||
void base_add_argument(std::string const &short_arg, std::string const &long_arg, std::string const &help_text,
|
void base_add_argument(std::string const &short_arg, std::string const &long_arg, std::string const &help_text,
|
||||||
@@ -373,14 +396,15 @@ namespace argument_parser {
|
|||||||
template <typename ActionType>
|
template <typename ActionType>
|
||||||
void base_add_positional_argument(std::string const &name, std::string const &help_text,
|
void base_add_positional_argument(std::string const &name, std::string const &help_text,
|
||||||
ActionType const &action, bool required,
|
ActionType const &action, bool required,
|
||||||
std::optional<int> position = std::nullopt) {
|
std::optional<int> position = std::nullopt, bool accumulator = false) {
|
||||||
assert_positional_not_exist(name);
|
assert_positional_not_exist(name);
|
||||||
int id = id_counter.fetch_add(1);
|
int id = id_counter.fetch_add(1);
|
||||||
argument arg(id, name, action);
|
argument arg(id, name, action);
|
||||||
set_argument_status(required, help_text, arg);
|
set_argument_status(required, help_text, arg);
|
||||||
arg.set_positional(true);
|
arg.set_positional(true);
|
||||||
|
arg.set_positional_accumulator(accumulator);
|
||||||
arg.set_position_index(position);
|
arg.set_position_index(position);
|
||||||
place_positional_argument(id, arg, name, position);
|
place_positional_argument(id, arg, name, position, accumulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StoreType>
|
template <typename StoreType>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ namespace argument_parser {
|
|||||||
public:
|
public:
|
||||||
fake_parser() = default;
|
fake_parser() = default;
|
||||||
fake_parser(std::string program_name, std::vector<std::string> const &arguments);
|
fake_parser(std::string program_name, std::vector<std::string> const &arguments);
|
||||||
fake_parser(std::string const &program_name, std::vector<std::string> &&arguments);
|
fake_parser(std::string program_name, std::vector<std::string> &&arguments);
|
||||||
fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments);
|
fake_parser(std::string program_name, std::initializer_list<std::string> const &arguments);
|
||||||
|
|
||||||
void set_program_name(std::string const &program_name);
|
void set_program_name(std::string const &program_name);
|
||||||
void set_parsed_arguments(std::vector<std::string> const &parsed_arguments);
|
void set_parsed_arguments(std::vector<std::string> const &parsed_arguments);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "traits.hpp"
|
#include "traits.hpp"
|
||||||
#include <argument_parser.hpp>
|
#include <argument_parser.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -23,7 +22,8 @@ namespace argument_parser::v2 {
|
|||||||
HelpText,
|
HelpText,
|
||||||
Action,
|
Action,
|
||||||
Required,
|
Required,
|
||||||
Reference
|
Reference,
|
||||||
|
Accumulate
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace flags {
|
namespace flags {
|
||||||
@@ -35,9 +35,27 @@ namespace argument_parser::v2 {
|
|||||||
constexpr static inline add_argument_flags Positional = add_argument_flags::Positional;
|
constexpr static inline add_argument_flags Positional = add_argument_flags::Positional;
|
||||||
constexpr static inline add_argument_flags Position = add_argument_flags::Position;
|
constexpr static inline add_argument_flags Position = add_argument_flags::Position;
|
||||||
constexpr static inline add_argument_flags Reference = add_argument_flags::Reference;
|
constexpr static inline add_argument_flags Reference = add_argument_flags::Reference;
|
||||||
|
constexpr static inline add_argument_flags Accumulate = add_argument_flags::Accumulate;
|
||||||
} // namespace flags
|
} // namespace flags
|
||||||
|
|
||||||
class base_parser : private argument_parser::base_parser {
|
namespace deducers {
|
||||||
|
template <typename, typename = void> struct has_value_type : std::false_type {};
|
||||||
|
template <typename T> struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> struct is_vector {
|
||||||
|
static constexpr bool test() {
|
||||||
|
if constexpr (has_value_type<T>::value) {
|
||||||
|
return std::is_same_v<T, std::vector<typename T::value_type, typename T::allocator_type>>;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> constexpr bool is_vector_v = is_vector<T>::test();
|
||||||
|
} // namespace deducers
|
||||||
|
|
||||||
|
class base_parser : argument_parser::base_parser {
|
||||||
public:
|
public:
|
||||||
template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool, int, T *>;
|
template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool, int, T *>;
|
||||||
using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool, int>;
|
using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool, int>;
|
||||||
@@ -104,7 +122,7 @@ namespace argument_parser::v2 {
|
|||||||
void prepare_help_flag(bool should_exit = true) {
|
void prepare_help_flag(bool should_exit = true) {
|
||||||
add_argument({{flags::ShortArgument, "h"},
|
add_argument({{flags::ShortArgument, "h"},
|
||||||
{flags::LongArgument, "help"},
|
{flags::LongArgument, "help"},
|
||||||
{flags::Action, helpers::make_non_parametered_action([this, should_exit]() {
|
{flags::Action, helpers::make_non_parametered_action([this, should_exit] {
|
||||||
this->display_help(this->current_conventions());
|
this->display_help(this->current_conventions());
|
||||||
if (should_exit) {
|
if (should_exit) {
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
@@ -127,12 +145,13 @@ namespace argument_parser::v2 {
|
|||||||
std::string short_arg, long_arg, help_text;
|
std::string short_arg, long_arg, help_text;
|
||||||
std::unique_ptr<action_base> action;
|
std::unique_ptr<action_base> action;
|
||||||
bool required = false;
|
bool required = false;
|
||||||
|
bool accumulates = false;
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::ShortArgument) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::ShortArgument)) {
|
||||||
found_params[extended_add_argument_flags::ShortArgument] = true;
|
found_params[extended_add_argument_flags::ShortArgument] = true;
|
||||||
short_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::ShortArgument), "short");
|
short_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::ShortArgument), "short");
|
||||||
}
|
}
|
||||||
if (argument_pairs.find(add_argument_flags::LongArgument) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::LongArgument)) {
|
||||||
found_params[extended_add_argument_flags::LongArgument] = true;
|
found_params[extended_add_argument_flags::LongArgument] = true;
|
||||||
long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long");
|
long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long");
|
||||||
if (short_arg.empty())
|
if (short_arg.empty())
|
||||||
@@ -142,20 +161,17 @@ namespace argument_parser::v2 {
|
|||||||
long_arg = "-";
|
long_arg = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::Action)) {
|
||||||
found_params[extended_add_argument_flags::Action] = true;
|
found_params[extended_add_argument_flags::Action] = true;
|
||||||
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
|
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
|
||||||
}
|
}
|
||||||
if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) {
|
help_text = read_help_text(argument_pairs);
|
||||||
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
|
required = read_required(argument_pairs);
|
||||||
}
|
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() &&
|
bool ref_mode = false;
|
||||||
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
|
|
||||||
required = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::Reference)) {
|
||||||
|
ref_mode = true;
|
||||||
if (!IsTyped) {
|
if (!IsTyped) {
|
||||||
throw std::logic_error("Reference argument must be typed");
|
throw std::logic_error("Reference argument must be typed");
|
||||||
}
|
}
|
||||||
@@ -165,14 +181,52 @@ namespace argument_parser::v2 {
|
|||||||
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
||||||
if (action) {
|
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<T>([ref](T const &t) { *ref = t; }).clone();
|
|
||||||
}
|
}
|
||||||
|
action = make_reference_action(ref);
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error("Reference argument must not be void");
|
throw std::logic_error("Reference argument must not be void");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_flag(argument_pairs, add_argument_flags::Accumulate)) {
|
||||||
|
if (!IsTyped)
|
||||||
|
throw std::logic_error("Accumulate argument must be typed");
|
||||||
|
|
||||||
|
found_params[extended_add_argument_flags::Action] = true;
|
||||||
|
accumulates = true;
|
||||||
|
if constexpr (!std::is_same_v<T, void>) {
|
||||||
|
if constexpr (!deducers::is_vector_v<T>) {
|
||||||
|
throw std::logic_error("Expected vector (type does not have value_type member)");
|
||||||
|
} else {
|
||||||
|
if (action && !ref_mode) {
|
||||||
|
throw std::logic_error("Cannot use both action and accumulate for the same argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
action = make_accumulate_action<T>(argument_pairs, ref_mode, short_arg, long_arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("Accumulate argument must not be void");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accumulates) {
|
||||||
|
if constexpr (!std::is_same_v<T, void> && deducers::is_vector_v<T>) {
|
||||||
|
if (suggest_candidate(found_params) == candidate_type::unknown) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Could not match any add argument overload to given parameters. Are you "
|
||||||
|
"missing some required parameter?");
|
||||||
|
}
|
||||||
|
if (help_text.empty()) {
|
||||||
|
help_text = "Accepts repeated values.";
|
||||||
|
}
|
||||||
|
|
||||||
|
base::add_argument<typename T::value_type>(
|
||||||
|
short_arg, long_arg, help_text,
|
||||||
|
*static_cast<parametered_action<typename T::value_type> *>(&(*action)), required);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto suggested_add = suggest_candidate(found_params);
|
auto suggested_add = suggest_candidate(found_params);
|
||||||
if (suggested_add == candidate_type::unknown) {
|
if (suggested_add == candidate_type::unknown) {
|
||||||
throw std::runtime_error("Could not match any add argument overload to given parameters. Are you "
|
throw std::runtime_error("Could not match any add argument overload to given parameters. Are you "
|
||||||
@@ -238,52 +292,67 @@ namespace argument_parser::v2 {
|
|||||||
default:
|
default:
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Could not match the arguments against any overload. The suggested candidate was: " +
|
"Could not match the arguments against any overload. The suggested candidate was: " +
|
||||||
std::to_string((int(suggested_add))));
|
std::to_string(static_cast<int>(suggested_add)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
||||||
void add_positional_argument_impl(ArgsMap const &argument_pairs) {
|
void add_positional_argument_impl(ArgsMap const &argument_pairs) {
|
||||||
std::string positional_name =
|
auto positional_name =
|
||||||
get_or_throw<std::string>(argument_pairs.at(add_argument_flags::Positional), "positional");
|
get_or_throw<std::string>(argument_pairs.at(add_argument_flags::Positional), "positional");
|
||||||
|
|
||||||
std::string help_text;
|
|
||||||
std::unique_ptr<action_base> action;
|
std::unique_ptr<action_base> action;
|
||||||
bool required = false;
|
bool required = false;
|
||||||
std::optional<int> position = std::nullopt;
|
bool ref_mode = false;
|
||||||
|
bool accumulates = false;
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::Action)) {
|
||||||
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
|
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
|
||||||
}
|
}
|
||||||
if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) {
|
std::string help_text = read_help_text(argument_pairs);
|
||||||
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
|
required = read_required(argument_pairs);
|
||||||
}
|
std::optional<int> position = read_position(argument_pairs);
|
||||||
if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() &&
|
|
||||||
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
|
|
||||||
required = true;
|
|
||||||
}
|
|
||||||
if (argument_pairs.find(add_argument_flags::Position) != argument_pairs.end()) {
|
|
||||||
position = get_or_throw<int>(argument_pairs.at(add_argument_flags::Position), "position");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) {
|
if (has_flag(argument_pairs, add_argument_flags::Reference)) {
|
||||||
|
ref_mode = true;
|
||||||
if (!IsTyped) {
|
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<T, void>) {
|
if constexpr (!std::is_same_v<T, void>) {
|
||||||
|
if (!has_flag(argument_pairs, add_argument_flags::Accumulate)) {
|
||||||
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
||||||
if (action) {
|
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<T>([ref](T const &t) { *ref = t; }).clone();
|
|
||||||
|
action = make_reference_action(ref);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error("Reference argument must not be void");
|
throw std::logic_error("Reference argument must not be void");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_flag(argument_pairs, add_argument_flags::Accumulate)) {
|
||||||
|
if (!IsTyped)
|
||||||
|
throw std::logic_error("Accumulate positional argument must be typed");
|
||||||
|
|
||||||
|
accumulates = true;
|
||||||
|
if constexpr (!std::is_same_v<T, void>) {
|
||||||
|
if constexpr (!deducers::is_vector_v<T>) {
|
||||||
|
throw std::logic_error("Expected vector (type does not have value_type member)");
|
||||||
|
} else {
|
||||||
|
if (action && !ref_mode) {
|
||||||
|
throw std::logic_error("Cannot use both action and accumulate for the same argument");
|
||||||
|
}
|
||||||
|
action = make_accumulate_action<T>(argument_pairs, ref_mode, positional_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("Accumulate argument must not be void");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (help_text.empty()) {
|
if (help_text.empty()) {
|
||||||
if constexpr (IsTyped) {
|
if constexpr (IsTyped) {
|
||||||
if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
|
if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
|
||||||
@@ -300,15 +369,24 @@ namespace argument_parser::v2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accumulates) {
|
||||||
|
if constexpr (!std::is_same_v<T, void> && deducers::is_vector_v<T>) {
|
||||||
|
base::add_positional_accumulator<typename T::value_type>(
|
||||||
|
positional_name, help_text,
|
||||||
|
*static_cast<parametered_action<typename T::value_type> *>(&(*action)), required, position);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr (IsTyped) {
|
if constexpr (IsTyped) {
|
||||||
if (action) {
|
if (action) {
|
||||||
base::add_positional_argument<T>(positional_name, help_text, *static_cast<ActionType *>(&(*action)),
|
base::add_positional_argument<T>(positional_name, help_text, *static_cast<ActionType *>(&(*action)),
|
||||||
required, position);
|
required, position);
|
||||||
} else {
|
} else {
|
||||||
base::template add_positional_argument<T>(positional_name, help_text, required, position);
|
base::add_positional_argument<T>(positional_name, help_text, required, position);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
base::template add_positional_argument<std::string>(positional_name, help_text, required, position);
|
base::add_positional_argument<std::string>(positional_name, help_text, required, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,11 +397,7 @@ namespace argument_parser::v2 {
|
|||||||
|
|
||||||
template <typename T, size_t S>
|
template <typename T, size_t S>
|
||||||
bool satisfies_at_least_one(std::array<T, S> const &arr, std::unordered_map<T, bool> const &map) {
|
bool satisfies_at_least_one(std::array<T, S> const &arr, std::unordered_map<T, bool> const &map) {
|
||||||
for (const auto &req : arr) {
|
return std::any_of(arr.begin(), arr.end(), [&map](T const &entry) { return map.find(entry) != map.end(); });
|
||||||
if (map.find(req) != map.end())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate_type suggest_candidate(std::unordered_map<extended_add_argument_flags, bool> const &available_vars) {
|
candidate_type suggest_candidate(std::unordered_map<extended_add_argument_flags, bool> const &available_vars) {
|
||||||
@@ -335,7 +409,6 @@ namespace argument_parser::v2 {
|
|||||||
if (available_vars.find(extended_add_argument_flags::Action) != available_vars.end()) {
|
if (available_vars.find(extended_add_argument_flags::Action) != available_vars.end()) {
|
||||||
if (available_vars.at(extended_add_argument_flags::IsTyped))
|
if (available_vars.at(extended_add_argument_flags::IsTyped))
|
||||||
return candidate_type::typed_action;
|
return candidate_type::typed_action;
|
||||||
else
|
|
||||||
return candidate_type::non_typed_action;
|
return candidate_type::non_typed_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,6 +417,143 @@ namespace argument_parser::v2 {
|
|||||||
return candidate_type::store_boolean;
|
return candidate_type::store_boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename ArgsMap> static bool has_flag(ArgsMap const &argument_pairs, add_argument_flags flag) {
|
||||||
|
return argument_pairs.find(flag) != argument_pairs.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ArgsMap> std::string read_help_text(ArgsMap const &argument_pairs) {
|
||||||
|
if (has_flag(argument_pairs, add_argument_flags::HelpText)) {
|
||||||
|
return get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ArgsMap> bool read_required(ArgsMap const &argument_pairs) {
|
||||||
|
return has_flag(argument_pairs, add_argument_flags::Required) &&
|
||||||
|
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ArgsMap> std::optional<int> read_position(ArgsMap const &argument_pairs) {
|
||||||
|
if (has_flag(argument_pairs, add_argument_flags::Position)) {
|
||||||
|
return get_or_throw<int>(argument_pairs.at(add_argument_flags::Position), "position");
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename T2, typename I>
|
||||||
|
std::variant<T, T2> get_either_or_throw(typed_flag_value<I> const &v, std::string_view key) {
|
||||||
|
if (auto p = std::get_if<T>(&v))
|
||||||
|
return *p;
|
||||||
|
if (auto p = std::get_if<T2>(&v))
|
||||||
|
return *p;
|
||||||
|
throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::unique_ptr<action_base> make_reference_action(T *target) {
|
||||||
|
return helpers::make_parametered_action<T>([target](T const &value) { *target = value; }).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector> std::unique_ptr<action_base> make_accumulate_ref_action(Vector *target) {
|
||||||
|
using Value = typename Vector::value_type;
|
||||||
|
return helpers::make_parametered_action<Value>(
|
||||||
|
[target](Value const &value) { target->emplace_back(value); })
|
||||||
|
.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector>
|
||||||
|
void store_accumulated_on_complete(std::string short_arg, std::string long_arg,
|
||||||
|
std::shared_ptr<Vector> accumulation_target) {
|
||||||
|
on_complete(
|
||||||
|
[this, short_arg = std::move(short_arg), long_arg = std::move(long_arg),
|
||||||
|
accumulation_target](auto const &) {
|
||||||
|
if (accumulation_target->empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto sid = this->find_argument_id(short_arg);
|
||||||
|
const auto lid = this->find_argument_id(long_arg);
|
||||||
|
|
||||||
|
if (const auto id = sid ? *sid : (lid ? *lid : -1); id != -1) {
|
||||||
|
this->ref_stored_arguments()[id] = *accumulation_target;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector>
|
||||||
|
void store_accumulated_on_complete(std::string positional_name, std::shared_ptr<Vector> accumulation_target) {
|
||||||
|
on_complete(
|
||||||
|
[this, positional_name = std::move(positional_name), accumulation_target](auto const &) {
|
||||||
|
if (accumulation_target->empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = this->find_argument_id(positional_name);
|
||||||
|
if (id.has_value()) {
|
||||||
|
this->ref_stored_arguments()[*id] = *accumulation_target;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename ArgsMap>
|
||||||
|
std::unique_ptr<action_base> make_accumulate_action(ArgsMap const &argument_pairs, bool ref_mode,
|
||||||
|
std::string const &short_arg, std::string const &long_arg) {
|
||||||
|
if (ref_mode) {
|
||||||
|
auto ref = get_or_throw<Vector *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
||||||
|
return make_accumulate_ref_action(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accumulate =
|
||||||
|
get_either_or_throw<Vector *, bool>(argument_pairs.at(add_argument_flags::Accumulate), "accumulate");
|
||||||
|
|
||||||
|
return std::visit(
|
||||||
|
[this, short_arg, long_arg](auto &&acc) -> std::unique_ptr<action_base> {
|
||||||
|
using V = std::decay_t<decltype(acc)>;
|
||||||
|
if constexpr (std::is_same_v<V, bool>) {
|
||||||
|
if (!acc) {
|
||||||
|
throw std::logic_error("Accumulate flag must be true when used as a bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accumulation_target = std::make_shared<Vector>();
|
||||||
|
store_accumulated_on_complete(short_arg, long_arg, accumulation_target);
|
||||||
|
return make_accumulate_ref_action(accumulation_target.get());
|
||||||
|
} else {
|
||||||
|
return make_accumulate_ref_action(acc);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accumulate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename ArgsMap>
|
||||||
|
std::unique_ptr<action_base> make_accumulate_action(ArgsMap const &argument_pairs, bool ref_mode,
|
||||||
|
std::string const &positional_name) {
|
||||||
|
if (ref_mode) {
|
||||||
|
auto ref = get_or_throw<Vector *>(argument_pairs.at(add_argument_flags::Reference), "reference");
|
||||||
|
return make_accumulate_ref_action(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accumulate =
|
||||||
|
get_either_or_throw<Vector *, bool>(argument_pairs.at(add_argument_flags::Accumulate), "accumulate");
|
||||||
|
|
||||||
|
return std::visit(
|
||||||
|
[this, positional_name](auto &&acc) -> std::unique_ptr<action_base> {
|
||||||
|
using V = std::decay_t<decltype(acc)>;
|
||||||
|
if constexpr (std::is_same_v<V, bool>) {
|
||||||
|
if (!acc) {
|
||||||
|
throw std::logic_error("Accumulate flag must be true when used as a bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accumulation_target = std::make_shared<Vector>();
|
||||||
|
store_accumulated_on_complete(positional_name, accumulation_target);
|
||||||
|
return make_accumulate_ref_action(accumulation_target.get());
|
||||||
|
} else {
|
||||||
|
return make_accumulate_ref_action(acc);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accumulate);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename I> T get_or_throw(typed_flag_value<I> const &v, std::string_view key) {
|
template <typename T, typename I> T get_or_throw(typed_flag_value<I> const &v, std::string_view key) {
|
||||||
if (auto p = std::get_if<T>(&v))
|
if (auto p = std::get_if<T>(&v))
|
||||||
return *p;
|
return *p;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// ReSharper disable CppFunctionIsNotImplemented
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef PARSING_TRAITS_HPP
|
#ifndef PARSING_TRAITS_HPP
|
||||||
#define PARSING_TRAITS_HPP
|
#define PARSING_TRAITS_HPP
|
||||||
@@ -61,10 +62,10 @@ namespace argument_parser::parsing_traits {
|
|||||||
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
|
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
|
||||||
|
|
||||||
std::array<char, total_len + 1> arr{};
|
std::array<char, total_len + 1> arr{};
|
||||||
|
// ReSharper disable once CppDFAUnreadVariable
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
auto append = [&](const hint_type s) {
|
||||||
auto append = [&](hint_type s) {
|
const std::string_view sv{s};
|
||||||
std::string_view sv{s};
|
|
||||||
for (char c : sv)
|
for (char c : sv)
|
||||||
arr[offset++] = c;
|
arr[offset++] = c;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace argument_parser {
|
|||||||
namespace v2 {
|
namespace v2 {
|
||||||
class macos_parser : public v2::base_parser {
|
class macos_parser : public v2::base_parser {
|
||||||
public:
|
public:
|
||||||
macos_parser(bool should_exit = true);
|
explicit macos_parser(bool should_exit = true);
|
||||||
using base_parser::display_help;
|
using base_parser::display_help;
|
||||||
};
|
};
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
namespace argument_parser::conventions::helpers {
|
namespace argument_parser::conventions::helpers {
|
||||||
std::string to_lower(std::string s) {
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_upper(std::string 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;
|
return s;
|
||||||
}
|
}
|
||||||
} // namespace argument_parser::conventions::helpers
|
} // namespace argument_parser::conventions::helpers
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ namespace argument_parser::conventions::implementations {
|
|||||||
parsed_argument gnu_argument_convention::get_argument(std::string const &raw) const {
|
parsed_argument gnu_argument_convention::get_argument(std::string const &raw) const {
|
||||||
if (starts_with(raw, long_prec()))
|
if (starts_with(raw, long_prec()))
|
||||||
return {argument_type::LONG, raw.substr(2)};
|
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)};
|
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."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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::pair<std::string, std::string> gnu_argument_convention::make_help_text(std::string const &short_arg,
|
||||||
std::string const &long_arg,
|
std::string const &long_arg,
|
||||||
bool requires_value) const {
|
bool const requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part;
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && !short_arg.empty()) {
|
||||||
s_part += short_prec() + short_arg;
|
s_part += short_prec() + short_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
s_part += " <value>";
|
s_part += " <value>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string l_part = "";
|
std::string l_part;
|
||||||
if (long_arg != "-" && long_arg != "") {
|
if (long_arg != "-" && !long_arg.empty()) {
|
||||||
l_part += long_prec() + long_arg;
|
l_part += long_prec() + long_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
l_part += " <value>";
|
l_part += " <value>";
|
||||||
@@ -65,18 +64,17 @@ namespace argument_parser::conventions::implementations {
|
|||||||
|
|
||||||
namespace argument_parser::conventions::implementations {
|
namespace argument_parser::conventions::implementations {
|
||||||
parsed_argument gnu_equal_argument_convention::get_argument(std::string const &raw) const {
|
parsed_argument gnu_equal_argument_convention::get_argument(std::string const &raw) const {
|
||||||
auto pos = raw.find('=');
|
const auto pos = raw.find('=');
|
||||||
auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
|
const auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
|
||||||
if (starts_with(arg, long_prec()))
|
if (starts_with(arg, long_prec()))
|
||||||
return {argument_type::LONG, arg.substr(2)};
|
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)};
|
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 {
|
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())
|
if (pos == std::string::npos || pos + 1 >= raw.size())
|
||||||
throw std::runtime_error("Expected value after '='.");
|
throw std::runtime_error("Expected value after '='.");
|
||||||
return raw.substr(pos + 1);
|
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::pair<std::string, std::string> gnu_equal_argument_convention::make_help_text(std::string const &short_arg,
|
||||||
std::string const &long_arg,
|
std::string const &long_arg,
|
||||||
bool requires_value) const {
|
bool const requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part;
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && !short_arg.empty()) {
|
||||||
s_part += short_prec() + short_arg;
|
s_part += short_prec() + short_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
s_part += "=<value>";
|
s_part += "=<value>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string l_part = "";
|
std::string l_part;
|
||||||
if (long_arg != "-" && long_arg != "") {
|
if (long_arg != "-" && !long_arg.empty()) {
|
||||||
l_part += long_prec() + long_arg;
|
l_part += long_prec() + long_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
l_part += "=<value>";
|
l_part += "=<value>";
|
||||||
|
|||||||
@@ -3,15 +3,14 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace argument_parser::conventions::implementations {
|
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 {
|
parsed_argument windows_argument_convention::get_argument(std::string const &raw) const {
|
||||||
if (raw.empty()) {
|
if (raw.empty()) {
|
||||||
return {argument_type::ERROR, "Empty argument token."};
|
return {argument_type::ERROR, "Empty argument token."};
|
||||||
}
|
}
|
||||||
const char c0 = raw[0];
|
const char c0 = raw[0];
|
||||||
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
|
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
|
||||||
if (!ok_prefix) {
|
|
||||||
return {argument_type::ERROR,
|
return {argument_type::ERROR,
|
||||||
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
||||||
: "Windows-style expects options to start with '/'."};
|
: "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::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg,
|
||||||
std::string const &long_arg,
|
std::string const &long_arg,
|
||||||
bool requires_value) const {
|
bool const requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part;
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && !short_arg.empty()) {
|
||||||
s_part += short_prec() + short_arg;
|
s_part += short_prec() + short_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
s_part += " <value>";
|
s_part += " <value>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string l_part = "";
|
std::string l_part;
|
||||||
if (long_arg != "-" && long_arg != "") {
|
if (long_arg != "-" && !long_arg.empty()) {
|
||||||
l_part += long_prec() + long_arg;
|
l_part += long_prec() + long_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
l_part += " <value>";
|
l_part += " <value>";
|
||||||
@@ -79,15 +78,14 @@ namespace argument_parser::conventions::implementations {
|
|||||||
} // namespace argument_parser::conventions::implementations
|
} // 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 {
|
parsed_argument windows_kv_argument_convention::get_argument(std::string const &raw) const {
|
||||||
if (raw.empty()) {
|
if (raw.empty()) {
|
||||||
return {argument_type::ERROR, "Empty argument token."};
|
return {argument_type::ERROR, "Empty argument token."};
|
||||||
}
|
}
|
||||||
const char c0 = raw[0];
|
const char c0 = raw[0];
|
||||||
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
|
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
|
||||||
if (!ok_prefix) {
|
|
||||||
return {argument_type::ERROR,
|
return {argument_type::ERROR,
|
||||||
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
|
||||||
: "Windows-style expects options to start with '/'."};
|
: "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::pair<std::string, std::string> windows_kv_argument_convention::make_help_text(std::string const &short_arg,
|
||||||
std::string const &long_arg,
|
std::string const &long_arg,
|
||||||
bool requires_value) const {
|
bool const requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part;
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && !short_arg.empty()) {
|
||||||
s_part += short_prec() + short_arg;
|
s_part += short_prec() + short_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
s_part += "=<value>, " + short_prec() + short_arg + ":<value>";
|
s_part += "=<value>, " + short_prec() + short_arg + ":<value>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string l_part = "";
|
std::string l_part;
|
||||||
if (long_arg != "-" && long_arg != "") {
|
if (long_arg != "-" && !long_arg.empty()) {
|
||||||
l_part += long_prec() + long_arg;
|
l_part += long_prec() + long_arg;
|
||||||
if (requires_value) {
|
if (requires_value) {
|
||||||
l_part += "=<value>, " + long_prec() + long_arg + ":<value>";
|
l_part += "=<value>, " + long_prec() + long_arg + ":<value>";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "argument_parser.hpp"
|
#include "argument_parser.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
|
|
||||||
class deferred_exec {
|
class deferred_exec {
|
||||||
public:
|
public:
|
||||||
deferred_exec(std::function<void()> const &func) : func(func) {}
|
explicit deferred_exec(std::function<void()> const &func) : func(func) {}
|
||||||
~deferred_exec() {
|
~deferred_exec() {
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
@@ -28,12 +29,12 @@ bool contains(std::unordered_map<std::string, int> const &map, std::string const
|
|||||||
|
|
||||||
namespace argument_parser {
|
namespace argument_parser {
|
||||||
argument::argument()
|
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)
|
argument::argument(const argument &other)
|
||||||
: id(other.id), name(other.name), action(other.action->clone()), required(other.required),
|
: id(other.id), name(other.name), action(other.action->clone()), required(other.required),
|
||||||
invoked(other.invoked), help_text(other.help_text), positional(other.positional),
|
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) {
|
argument &argument::operator=(const argument &other) {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
@@ -44,6 +45,7 @@ namespace argument_parser {
|
|||||||
invoked = other.invoked;
|
invoked = other.invoked;
|
||||||
help_text = other.help_text;
|
help_text = other.help_text;
|
||||||
positional = other.positional;
|
positional = other.positional;
|
||||||
|
positional_accumulator = other.positional_accumulator;
|
||||||
position_index = other.position_index;
|
position_index = other.position_index;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
@@ -69,11 +71,11 @@ namespace argument_parser {
|
|||||||
return help_text;
|
return help_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void argument::set_required(bool val) {
|
void argument::set_required(const bool val) {
|
||||||
required = val;
|
required = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void argument::set_invoked(bool val) {
|
void argument::set_invoked(const bool val) {
|
||||||
invoked = val;
|
invoked = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,21 +87,37 @@ namespace argument_parser {
|
|||||||
return positional;
|
return positional;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool argument::is_positional_accumulator() const {
|
||||||
|
return positional_accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<int> argument::get_position_index() const {
|
std::optional<int> argument::get_position_index() const {
|
||||||
return position_index;
|
return position_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void argument::set_positional(bool val) {
|
void argument::set_positional(const bool val) {
|
||||||
positional = 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;
|
position_index = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void base_parser::on_complete(std::function<void(base_parser const &)> const &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);
|
on_complete_events.emplace_back(handler);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
base_parser::build_help_text(std::initializer_list<conventions::convention const *const> convention_types) const {
|
base_parser::build_help_text(std::initializer_list<conventions::convention const *const> convention_types) const {
|
||||||
@@ -112,8 +130,7 @@ namespace argument_parser {
|
|||||||
auto name_it = reverse_positional_names.find(pos_id);
|
auto name_it = reverse_positional_names.find(pos_id);
|
||||||
if (name_it == reverse_positional_names.end())
|
if (name_it == reverse_positional_names.end())
|
||||||
continue;
|
continue;
|
||||||
auto const &arg = argument_map.at(pos_id);
|
if (auto const &arg = argument_map.at(pos_id); arg.is_required()) {
|
||||||
if (arg.is_required()) {
|
|
||||||
ss << " <" << name_it->second << ">";
|
ss << " <" << name_it->second << ">";
|
||||||
} else {
|
} else {
|
||||||
ss << " [" << name_it->second << "]";
|
ss << " [" << name_it->second << "]";
|
||||||
@@ -143,8 +160,8 @@ namespace argument_parser {
|
|||||||
std::unordered_set<std::string> hasOnce;
|
std::unordered_set<std::string> hasOnce;
|
||||||
for (auto const &convention : convention_types) {
|
for (auto const &convention : convention_types) {
|
||||||
auto generatedParts = convention->make_help_text(short_arg, long_arg, arg.expects_parameter());
|
auto generatedParts = convention->make_help_text(short_arg, long_arg, arg.expects_parameter());
|
||||||
std::string combined = generatedParts.first + "|" + generatedParts.second;
|
if (std::string combined = generatedParts.first + "|" + generatedParts.second;
|
||||||
if (hasOnce.find(combined) == hasOnce.end()) {
|
hasOnce.find(combined) == hasOnce.end()) {
|
||||||
parts.push_back(generatedParts);
|
parts.push_back(generatedParts);
|
||||||
hasOnce.insert(combined);
|
hasOnce.insert(combined);
|
||||||
|
|
||||||
@@ -155,24 +172,24 @@ namespace argument_parser {
|
|||||||
max_long_len = generatedParts.second.length();
|
max_long_len = generatedParts.second.length();
|
||||||
}
|
}
|
||||||
} else {
|
} 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});
|
help_lines.push_back({parts, arg.help_text});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!help_lines.empty()) {
|
if (!help_lines.empty()) {
|
||||||
for (auto const &line : help_lines) {
|
for (const auto &[convention_parts, desc] : help_lines) {
|
||||||
ss << "\t";
|
ss << "\t";
|
||||||
for (size_t i = 0; i < line.convention_parts.size(); ++i) {
|
for (size_t i = 0; i < convention_parts.size(); ++i) {
|
||||||
auto const &parts = line.convention_parts[i];
|
const auto &[fst, snd] = convention_parts[i];
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
ss << " ";
|
ss << " ";
|
||||||
}
|
}
|
||||||
ss << std::left << std::setw(static_cast<int>(max_short_len)) << parts.first << " "
|
ss << std::left << std::setw(static_cast<int>(max_short_len)) << fst << " "
|
||||||
<< std::setw(static_cast<int>(max_long_len)) << parts.second;
|
<< 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) {
|
for (auto const &pos_id : positional_arguments) {
|
||||||
if (pos_id == -1)
|
if (pos_id == -1)
|
||||||
continue;
|
continue;
|
||||||
auto name_it = reverse_positional_names.find(pos_id);
|
if (auto name_it = reverse_positional_names.find(pos_id); name_it != reverse_positional_names.end()) {
|
||||||
if (name_it != reverse_positional_names.end()) {
|
if (size_t display_len = name_it->second.length() + 2; display_len > max_pos_name_len)
|
||||||
size_t display_len = name_it->second.length() + 2; // for < >
|
|
||||||
if (display_len > max_pos_name_len)
|
|
||||||
max_pos_name_len = display_len;
|
max_pos_name_len = display_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,37 +223,32 @@ namespace argument_parser {
|
|||||||
|
|
||||||
argument &base_parser::get_argument(conventions::parsed_argument const &arg) {
|
argument &base_parser::get_argument(conventions::parsed_argument const &arg) {
|
||||||
if (arg.first == conventions::argument_type::LONG) {
|
if (arg.first == conventions::argument_type::LONG) {
|
||||||
auto long_pos = long_arguments.find(arg.second);
|
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
|
||||||
if (long_pos != long_arguments.end())
|
|
||||||
return argument_map.at(long_pos->second);
|
return argument_map.at(long_pos->second);
|
||||||
} else if (arg.first == conventions::argument_type::SHORT) {
|
} else if (arg.first == conventions::argument_type::SHORT) {
|
||||||
auto short_pos = short_arguments.find(arg.second);
|
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
|
||||||
if (short_pos != short_arguments.end())
|
|
||||||
return argument_map.at(short_pos->second);
|
return argument_map.at(short_pos->second);
|
||||||
} else if (arg.first == conventions::argument_type::INTERCHANGABLE) {
|
} else if (arg.first == conventions::argument_type::INTERCHANGABLE) {
|
||||||
auto long_pos = long_arguments.find(arg.second);
|
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
|
||||||
if (long_pos != long_arguments.end())
|
|
||||||
return argument_map.at(long_pos->second);
|
return argument_map.at(long_pos->second);
|
||||||
auto short_pos = short_arguments.find(arg.second);
|
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
|
||||||
if (short_pos != short_arguments.end())
|
|
||||||
return argument_map.at(short_pos->second);
|
return argument_map.at(short_pos->second);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Unknown argument: " + arg.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()) {
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool base_parser::test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
|
bool
|
||||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
base_parser::test_conventions(const std::initializer_list<conventions::convention const *const> convention_types,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help,
|
||||||
std::optional<argument> &found_help, std::vector<std::string>::iterator &it,
|
std::vector<std::string>::iterator &it, std::stringstream &error_stream) {
|
||||||
std::stringstream &error_stream) {
|
|
||||||
|
|
||||||
std::string current_argument = *it;
|
const std::string current_argument = *it;
|
||||||
|
|
||||||
for (auto const &convention_type : convention_types) {
|
for (auto const &convention_type : convention_types) {
|
||||||
auto extracted = convention_type->get_argument(current_argument);
|
auto extracted = convention_type->get_argument(current_argument);
|
||||||
@@ -256,16 +266,17 @@ namespace argument_parser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
found_arguments.emplace_back(extracted.second, corresponding_argument);
|
found_argument found{extracted.second, corresponding_argument};
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
values_for_arguments[extracted.second] =
|
found.value =
|
||||||
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
found_arguments.emplace_back(std::move(found));
|
||||||
return true;
|
return true;
|
||||||
} catch (const std::runtime_error &e) {
|
} 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";
|
||||||
@@ -275,10 +286,9 @@ namespace argument_parser {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void base_parser::extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
|
void
|
||||||
std::unordered_map<std::string, std::string> &values_for_arguments,
|
base_parser::extract_arguments(const std::initializer_list<conventions::convention const *const> convention_types,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help) {
|
||||||
std::optional<argument> &found_help) {
|
|
||||||
|
|
||||||
size_t next_positional_index = 0;
|
size_t next_positional_index = 0;
|
||||||
bool force_positional = false;
|
bool force_positional = false;
|
||||||
@@ -290,29 +300,30 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (force_positional) {
|
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 + "\"");
|
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);
|
argument &pos_arg = argument_map.at(arg_id);
|
||||||
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
||||||
found_arguments.emplace_back(pos_name, pos_arg);
|
found_arguments.push_back({pos_name, pos_arg, *it});
|
||||||
values_for_arguments[pos_name] = *it;
|
if (!pos_arg.is_positional_accumulator()) {
|
||||||
next_positional_index++;
|
next_positional_index = *slot + 1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream error_stream;
|
if (std::stringstream error_stream;
|
||||||
|
!test_conventions(convention_types, found_arguments, found_help, it, error_stream)) {
|
||||||
if (!test_conventions(convention_types, values_for_arguments, found_arguments, found_help, it,
|
if (auto slot = next_positional_slot(next_positional_index); slot.has_value()) {
|
||||||
error_stream)) {
|
int arg_id = positional_arguments[*slot];
|
||||||
if (next_positional_index < positional_arguments.size()) {
|
|
||||||
int arg_id = positional_arguments[next_positional_index];
|
|
||||||
argument &pos_arg = argument_map.at(arg_id);
|
argument &pos_arg = argument_map.at(arg_id);
|
||||||
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
std::string const &pos_name = reverse_positional_names.at(arg_id);
|
||||||
found_arguments.emplace_back(pos_name, pos_arg);
|
found_arguments.push_back({pos_name, pos_arg, *it});
|
||||||
values_for_arguments[pos_name] = *it;
|
if (!pos_arg.is_positional_accumulator()) {
|
||||||
next_positional_index++;
|
next_positional_index = *slot + 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
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());
|
||||||
@@ -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 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);
|
size_t pos = text.find(placeholder);
|
||||||
|
|
||||||
while (pos != std::string::npos) {
|
while (pos != std::string::npos) {
|
||||||
@@ -332,8 +343,7 @@ namespace argument_parser {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void base_parser::invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
|
void base_parser::invoke_arguments(std::vector<found_argument> &found_arguments,
|
||||||
std::vector<std::pair<std::string, argument>> &found_arguments,
|
|
||||||
std::optional<argument> const &found_help) {
|
std::optional<argument> const &found_help) {
|
||||||
|
|
||||||
if (found_help) {
|
if (found_help) {
|
||||||
@@ -342,15 +352,15 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream error_stream;
|
std::stringstream error_stream;
|
||||||
for (auto &[key, value] : found_arguments) {
|
for (auto &[key, arg, value] : found_arguments) {
|
||||||
try {
|
try {
|
||||||
if (value.expects_parameter()) {
|
if (arg.expects_parameter()) {
|
||||||
value.action->invoke_with_parameter(values_for_arguments.at(key));
|
arg.action->invoke_with_parameter(value.value());
|
||||||
} else {
|
} else {
|
||||||
value.action->invoke();
|
arg.action->invoke();
|
||||||
}
|
}
|
||||||
value.set_invoked(true);
|
arg.set_invoked(true);
|
||||||
argument_map.at(value.id).set_invoked(true);
|
argument_map.at(arg.id).set_invoked(true);
|
||||||
} catch (const std::runtime_error &e) {
|
} catch (const std::runtime_error &e) {
|
||||||
std::string err{e.what()};
|
std::string err{e.what()};
|
||||||
err = replace_var(err, "KEY", "for " + key);
|
err = replace_var(err, "KEY", "for " + key);
|
||||||
@@ -358,43 +368,42 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error_message = error_stream.str();
|
if (const std::string error_message = error_stream.str(); !error_message.empty()) {
|
||||||
if (!error_message.empty()) {
|
|
||||||
throw std::runtime_error(error_message);
|
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();
|
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);
|
this->current_conventions(convention_types);
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> values_for_arguments;
|
std::vector<found_argument> found_arguments;
|
||||||
std::vector<std::pair<std::string, argument>> found_arguments;
|
|
||||||
std::optional<argument> found_help = std::nullopt;
|
std::optional<argument> found_help = std::nullopt;
|
||||||
|
|
||||||
extract_arguments(convention_types, values_for_arguments, found_arguments, found_help);
|
extract_arguments(convention_types, found_arguments, found_help);
|
||||||
invoke_arguments(values_for_arguments, found_arguments, found_help);
|
invoke_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();
|
||||||
}
|
}
|
||||||
|
|
||||||
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::cout << build_help_text(convention_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> base_parser::find_argument_id(std::string const &arg) const {
|
std::optional<int> base_parser::find_argument_id(std::string const &arg) const {
|
||||||
auto long_pos = long_arguments.find(arg);
|
const auto long_pos = long_arguments.find(arg);
|
||||||
auto short_post = short_arguments.find(arg);
|
const auto short_post = short_arguments.find(arg);
|
||||||
|
|
||||||
if (long_pos != long_arguments.end())
|
if (long_pos != long_arguments.end())
|
||||||
return long_pos->second;
|
return long_pos->second;
|
||||||
if (short_post != short_arguments.end())
|
if (short_post != short_arguments.end())
|
||||||
return short_post->second;
|
return short_post->second;
|
||||||
|
|
||||||
auto pos_it = positional_name_map.find(arg);
|
if (const auto pos_it = positional_name_map.find(arg); pos_it != positional_name_map.end())
|
||||||
if (pos_it != positional_name_map.end())
|
|
||||||
return pos_it->second;
|
return pos_it->second;
|
||||||
|
|
||||||
return std::nullopt;
|
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_required(is_required);
|
||||||
arg.set_help_text(help_text);
|
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) {
|
std::string const &long_arg) {
|
||||||
argument_map[id] = arg;
|
argument_map[id] = arg;
|
||||||
if (short_arg != "-") {
|
if (short_arg != "-") {
|
||||||
@@ -430,29 +439,81 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void base_parser::place_positional_argument(int id, argument const &arg, std::string const &name,
|
void base_parser::assert_can_place_positional(const int id, const std::optional<int> position,
|
||||||
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;
|
argument_map[id] = arg;
|
||||||
positional_name_map[name] = id;
|
positional_name_map[name] = id;
|
||||||
reverse_positional_names[id] = name;
|
reverse_positional_names[id] = name;
|
||||||
|
|
||||||
if (position.has_value()) {
|
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()) {
|
if (idx > positional_arguments.size()) {
|
||||||
positional_arguments.resize(idx + 1, -1);
|
positional_arguments.resize(idx + 1, -1);
|
||||||
}
|
}
|
||||||
if (idx < positional_arguments.size() && positional_arguments[idx] != -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()) {
|
if (idx == positional_arguments.size()) {
|
||||||
positional_arguments.push_back(id);
|
positional_arguments.push_back(id);
|
||||||
} else {
|
} else {
|
||||||
positional_arguments[idx] = id;
|
positional_arguments[idx] = id;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto empty_slot = std::find(positional_arguments.begin(), positional_arguments.end(), -1);
|
||||||
|
empty_slot != positional_arguments.end()) {
|
||||||
|
*empty_slot = id;
|
||||||
} else {
|
} else {
|
||||||
positional_arguments.push_back(id);
|
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 get_one_name(std::string const &short_name, std::string const &long_name) {
|
||||||
std::string res{};
|
std::string res{};
|
||||||
@@ -500,15 +561,15 @@ namespace argument_parser {
|
|||||||
} else {
|
} else {
|
||||||
std::cerr << "\t" << get_one_name(s, l) << ": must be provided as one of [";
|
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) {
|
for (auto it = convention_types.begin(); it != convention_types.end(); ++it) {
|
||||||
auto generatedParts = (*it)->make_help_text(s, l, p);
|
auto [short_part, long_part] = (*it)->make_help_text(s, l, p);
|
||||||
std::string help_str = generatedParts.first;
|
std::string help_str = short_part;
|
||||||
if (!generatedParts.first.empty() && !generatedParts.second.empty()) {
|
if (!short_part.empty() && !long_part.empty()) {
|
||||||
help_str += " ";
|
help_str += " ";
|
||||||
}
|
}
|
||||||
help_str += generatedParts.second;
|
help_str += long_part;
|
||||||
|
|
||||||
size_t last_not_space = help_str.find_last_not_of(" \t");
|
if (size_t last_not_space = help_str.find_last_not_of(" \t");
|
||||||
if (last_not_space != std::string::npos) {
|
last_not_space != std::string::npos) {
|
||||||
help_str.erase(last_not_space + 1);
|
help_str.erase(last_not_space + 1);
|
||||||
}
|
}
|
||||||
std::cerr << help_str;
|
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::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) {
|
void fake_parser::set_program_name(std::string const &program_name) {
|
||||||
this->program_name = program_name;
|
this->program_name = program_name;
|
||||||
@@ -24,22 +24,22 @@ namespace argument_parser {
|
|||||||
|
|
||||||
namespace v2 {
|
namespace v2 {
|
||||||
fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) {
|
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;
|
ref_parsed_args() = arguments;
|
||||||
prepare_help_flag(false);
|
prepare_help_flag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fake_parser::fake_parser(std::string const &program_name, std::vector<std::string> &&arguments) {
|
fake_parser::fake_parser(std::string program_name, std::vector<std::string> &&arguments) {
|
||||||
set_program_name(program_name);
|
base_parser::set_program_name(std::move(program_name));
|
||||||
ref_parsed_args() = std::move(arguments);
|
ref_parsed_args() = std::move(arguments);
|
||||||
prepare_help_flag(false);
|
prepare_help_flag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
|
fake_parser::fake_parser(std::string program_name, std::initializer_list<std::string> const &arguments)
|
||||||
: fake_parser(program_name, std::vector<std::string>(arguments)) {}
|
: fake_parser(std::move(program_name), std::vector(arguments)) {}
|
||||||
|
|
||||||
void fake_parser::set_program_name(std::string const &program_name) {
|
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) {
|
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) \
|
#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \
|
||||||
do { \
|
do { \
|
||||||
const int argc_name = *_NSGetArgc(); \
|
const int (argc_name) = *_NSGetArgc(); \
|
||||||
if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \
|
if (char **(argv_name) = *_NSGetArgv(); (argc_name) > 0 && (argv_name) != nullptr && (argv_name)[0] != nullptr) { \
|
||||||
do { \
|
do { \
|
||||||
before_for; \
|
(before_for); \
|
||||||
} while (false); \
|
} while (false); \
|
||||||
for (int i = 1; i < argc_name; ++i) { \
|
for (int i = 1; i < (argc_name); ++i) { \
|
||||||
for_body \
|
{for_body} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
@@ -26,7 +26,7 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace v2 {
|
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]), {
|
MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), {
|
||||||
if (argv[i] != nullptr)
|
if (argv[i] != nullptr)
|
||||||
ref_parsed_args().emplace_back(argv[i]);
|
ref_parsed_args().emplace_back(argv[i]);
|
||||||
|
|||||||
Reference in New Issue
Block a user