mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-05-28 20:08:10 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21ca1f638b | |||
|
|
2b2a0df933 |
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
#include <argparse>
|
#include <argparse>
|
||||||
#include <gnu_argument_convention.hpp>
|
#include <gnu_argument_convention.hpp>
|
||||||
#include <macros.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <macros.h>
|
||||||
#include <parser_v2.hpp>
|
#include <parser_v2.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <traits.hpp>
|
#include <traits.hpp>
|
||||||
@@ -11,7 +11,7 @@ using argument = argument_parser::builder::argument<>;
|
|||||||
|
|
||||||
using argument_parser::parsing_traits::hint_type;
|
using argument_parser::parsing_traits::hint_type;
|
||||||
|
|
||||||
auto echo(std::string const& s) -> void {
|
auto echo(std::string const &s) -> void {
|
||||||
std::cout << s << '\n';
|
std::cout << s << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,10 +19,9 @@ 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>
|
template <typename T> class parser_trait<std::vector<T>> {
|
||||||
class parser_trait<std::vector<T>> {
|
public:
|
||||||
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);
|
||||||
|
|
||||||
@@ -33,16 +32,11 @@ class parser_trait<std::vector<T>> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARGPARSE_TRAIT_FORMAT_HINT = concat<
|
ARGPARSE_TRAIT_FORMAT_HINT = concat<hint_provider<&parser_trait<T>::format_hint>, hint_provider<&comma>,
|
||||||
hint_provider<&parser_trait<T>::format_hint>,
|
hint_provider<&parser_trait<T>::format_hint>>;
|
||||||
hint_provider<&comma>,
|
|
||||||
hint_provider<&parser_trait<T>::format_hint>
|
|
||||||
>;
|
|
||||||
|
|
||||||
ARGPARSE_TRAIT_PURPOSE_HINT = concat<
|
ARGPARSE_TRAIT_PURPOSE_HINT =
|
||||||
hint_provider<&vector_purpose_hint>,
|
concat<hint_provider<&vector_purpose_hint>, hint_provider<&parser_trait<T>::purpose_hint>>;
|
||||||
hint_provider<&parser_trait<T>::purpose_hint>
|
|
||||||
>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using namespace argument_parser::v2::flags;
|
using namespace argument_parser::v2::flags;
|
||||||
@@ -54,9 +48,7 @@ auto main() -> int {
|
|||||||
.positional("count")
|
.positional("count")
|
||||||
.position(1)
|
.position(1)
|
||||||
.help_text("How many times to repeat the action.")
|
.help_text("How many times to repeat the action.")
|
||||||
.action<int>([](int const& count) {
|
.action<int>([](int const &count) { std::cout << "count action configured for " << count << '\n'; })
|
||||||
std::cout << "count action configured for " << count << '\n';
|
|
||||||
})
|
|
||||||
.build(parser);
|
.build(parser);
|
||||||
|
|
||||||
int captured_value = 0;
|
int captured_value = 0;
|
||||||
@@ -79,10 +71,7 @@ auto main() -> int {
|
|||||||
// {Reference, &captured_value},
|
// {Reference, &captured_value},
|
||||||
// });
|
// });
|
||||||
|
|
||||||
argument::start()
|
argument::start().short_argument("q").help_text("Store a boolean flag.").build(parser);
|
||||||
.short_argument("q")
|
|
||||||
.help_text("Store a boolean flag.")
|
|
||||||
.build(parser);
|
|
||||||
|
|
||||||
// argument::start()
|
// argument::start()
|
||||||
// .long_argument("regex")
|
// .long_argument("regex")
|
||||||
@@ -100,16 +89,14 @@ auto main() -> int {
|
|||||||
argument::start()
|
argument::start()
|
||||||
.long_argument("vecstr")
|
.long_argument("vecstr")
|
||||||
.short_argument("vs")
|
.short_argument("vs")
|
||||||
.action<std::vector<int>>([](std::vector<int> const& vecstr) {
|
.action<std::vector<int>>([](std::vector<int> const &vecstr) {
|
||||||
for (auto const& str : vecstr) {
|
for (auto const &str : vecstr) {
|
||||||
std::cout << str << '\n';
|
std::cout << str << '\n';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build(parser);
|
.build(parser);
|
||||||
|
|
||||||
parser.handle_arguments({
|
parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention});
|
||||||
&argument_parser::conventions::gnu_argument_convention
|
|
||||||
});
|
|
||||||
|
|
||||||
std::cout << "captured value: " << captured_value << '\n';
|
std::cout << "captured value: " << captured_value << '\n';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ARGPARSE_HPP
|
#ifndef ARGPARSE_HPP
|
||||||
#define ARGPARSE_HPP
|
#define ARGPARSE_HPP
|
||||||
|
#include "macros.h"
|
||||||
|
#include <argument_builder.hpp>
|
||||||
#include <argument_parser.hpp>
|
#include <argument_parser.hpp>
|
||||||
#include <parser_v2.hpp>
|
#include <parser_v2.hpp>
|
||||||
#include <argument_builder.hpp>
|
|
||||||
#include "macros.h"
|
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <linux_parser.hpp>
|
#include <linux_parser.hpp>
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ namespace argument_parser::conventions {
|
|||||||
virtual std::string name() const = 0;
|
virtual std::string name() const = 0;
|
||||||
virtual std::string short_prec() const = 0;
|
virtual std::string short_prec() const = 0;
|
||||||
virtual std::string long_prec() const = 0;
|
virtual std::string long_prec() const = 0;
|
||||||
virtual std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
|
virtual std::pair<std::string, std::string>
|
||||||
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;
|
virtual std::vector<convention_features> get_features() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -2,31 +2,21 @@
|
|||||||
|
|
||||||
#include "argument_parser.hpp"
|
#include "argument_parser.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
|
||||||
#include <parser_v2.hpp>
|
#include <parser_v2.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#ifndef ARGUMENT_PARSER_PARSER_V3_HPP
|
#ifndef ARGUMENT_PARSER_PARSER_V3_HPP
|
||||||
#define ARGUMENT_PARSER_PARSER_V3_HPP
|
#define ARGUMENT_PARSER_PARSER_V3_HPP
|
||||||
|
|
||||||
namespace argument_parser::builder {
|
namespace argument_parser::builder {
|
||||||
|
|
||||||
class non_type {};
|
class non_type {};
|
||||||
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 {
|
enum class value_mode { unresolved, store, flag, reference, nonparametered_action, parametered_action };
|
||||||
unresolved,
|
|
||||||
store,
|
|
||||||
flag,
|
|
||||||
reference,
|
|
||||||
nonparametered_action,
|
|
||||||
parametered_action
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class extra_capability : unsigned {
|
enum class extra_capability : unsigned { Store = static_cast<unsigned>(v2_flag::Reference) + 1, Flag };
|
||||||
Store = static_cast<unsigned>(v2_flag::Reference) + 1,
|
|
||||||
Flag
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr auto bit(v2_flag flag) -> mask_type {
|
constexpr auto bit(v2_flag flag) -> mask_type {
|
||||||
return mask_type{1} << static_cast<unsigned>(flag);
|
return mask_type{1} << static_cast<unsigned>(flag);
|
||||||
@@ -48,8 +38,8 @@ namespace builder_mask {
|
|||||||
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 | store | flag;
|
||||||
constexpr mask_type initial = short_argument | long_argument | positional | help_text | action | required |
|
constexpr mask_type initial =
|
||||||
reference | store | flag;
|
short_argument | long_argument | positional | help_text | action | required | reference | 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;
|
||||||
@@ -70,11 +60,10 @@ namespace builder_mask {
|
|||||||
constexpr auto is_buildable(mask_type mask) -> bool {
|
constexpr auto is_buildable(mask_type mask) -> bool {
|
||||||
return has_selected_identifier(mask);
|
return has_selected_identifier(mask);
|
||||||
}
|
}
|
||||||
} // namespace builder_mask
|
} // namespace builder_mask
|
||||||
|
|
||||||
template<builder_mask::mask_type mask = builder_mask::initial, typename store_type = non_type>
|
template <builder_mask::mask_type mask = builder_mask::initial, typename store_type = non_type> class argument {
|
||||||
class argument {
|
public:
|
||||||
public:
|
|
||||||
using mask_type = builder_mask::mask_type;
|
using mask_type = builder_mask::mask_type;
|
||||||
using v2_flag = argument_parser::v2::add_argument_flags;
|
using v2_flag = argument_parser::v2::add_argument_flags;
|
||||||
using value_mode = builder_mask::value_mode;
|
using value_mode = builder_mask::value_mode;
|
||||||
@@ -83,13 +72,14 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::short_argument), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::short_argument), int> = 0>
|
||||||
auto short_argument(std::string short_name) const
|
auto short_argument(std::string short_name) const
|
||||||
-> argument<builder_mask::replace(current_mask, builder_mask::short_argument | builder_mask::positional |
|
-> argument<builder_mask::replace(current_mask, builder_mask::short_argument | builder_mask::positional |
|
||||||
builder_mask::position),
|
builder_mask::position),
|
||||||
store_type> {
|
store_type> {
|
||||||
using next_argument = argument<builder_mask::replace(current_mask,
|
using next_argument =
|
||||||
builder_mask::short_argument | builder_mask::positional |
|
argument<builder_mask::replace(current_mask, builder_mask::short_argument | builder_mask::positional |
|
||||||
builder_mask::position),
|
builder_mask::position),
|
||||||
store_type>;
|
store_type>;
|
||||||
|
|
||||||
@@ -98,13 +88,14 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::long_argument), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::long_argument), int> = 0>
|
||||||
auto long_argument(std::string long_name) const
|
auto long_argument(std::string long_name) const
|
||||||
-> argument<builder_mask::replace(current_mask, builder_mask::long_argument | builder_mask::positional |
|
-> argument<builder_mask::replace(current_mask, builder_mask::long_argument | builder_mask::positional |
|
||||||
builder_mask::position),
|
builder_mask::position),
|
||||||
store_type> {
|
store_type> {
|
||||||
using next_argument = argument<builder_mask::replace(current_mask,
|
using next_argument =
|
||||||
builder_mask::long_argument | builder_mask::positional |
|
argument<builder_mask::replace(current_mask, builder_mask::long_argument | builder_mask::positional |
|
||||||
builder_mask::position),
|
builder_mask::position),
|
||||||
store_type>;
|
store_type>;
|
||||||
|
|
||||||
@@ -113,7 +104,8 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::positional), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::positional), int> = 0>
|
||||||
auto positional(std::string positional_name) const
|
auto positional(std::string positional_name) const
|
||||||
-> argument<builder_mask::replace(current_mask,
|
-> argument<builder_mask::replace(current_mask,
|
||||||
builder_mask::short_argument | builder_mask::long_argument |
|
builder_mask::short_argument | builder_mask::long_argument |
|
||||||
@@ -132,8 +124,10 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::position), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
auto position(int index) const -> argument<builder_mask::remove(current_mask, builder_mask::position), store_type> {
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::position), int> = 0>
|
||||||
|
auto position(int index) const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::position), store_type> {
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::position), store_type>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::position), store_type>;
|
||||||
|
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
@@ -141,8 +135,10 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::help_text), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
auto help_text(std::string help) const -> argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type> {
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::help_text), int> = 0>
|
||||||
|
auto help_text(std::string help) const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type> {
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type>;
|
||||||
|
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
@@ -150,8 +146,10 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::required), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
auto required(bool value = true) const -> argument<builder_mask::remove(current_mask, builder_mask::required), store_type> {
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::required), int> = 0>
|
||||||
|
auto required(bool value = true) const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::required), store_type> {
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::required), store_type>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::required), store_type>;
|
||||||
|
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
@@ -159,9 +157,11 @@ public:
|
|||||||
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::store), int> = 0>
|
template <typename T = std::string, mask_type current_mask = mask,
|
||||||
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::store), int> = 0>
|
||||||
auto store() const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
|
auto store() const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
|
||||||
static_assert(!std::is_same_v<T, void>, "store<void>() is not supported. Use flag() for boolean-style arguments.");
|
static_assert(!std::is_same_v<T, void>,
|
||||||
|
"store<void>() is not supported. Use flag() for boolean-style arguments.");
|
||||||
|
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
@@ -169,7 +169,8 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::flag), int> = 0>
|
template <mask_type current_mask = mask,
|
||||||
|
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> {
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), bool>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), bool>;
|
||||||
|
|
||||||
@@ -178,9 +179,10 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::reference), int> = 0,
|
template <mask_type current_mask = mask,
|
||||||
typename T>
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::reference), int> = 0, typename T>
|
||||||
auto reference(T& value) const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
|
auto reference(T &value) const
|
||||||
|
-> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
||||||
|
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
@@ -189,10 +191,10 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0,
|
template <mask_type current_mask = mask,
|
||||||
typename Callable>
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0, typename Callable>
|
||||||
auto action(Callable&& handler) const
|
auto action(Callable &&handler) const -> std::enable_if_t<
|
||||||
-> std::enable_if_t<std::is_invocable_r_v<void, Callable>,
|
std::is_invocable_r_v<void, Callable>,
|
||||||
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>> {
|
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>> {
|
||||||
using next_argument =
|
using next_argument =
|
||||||
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>;
|
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>;
|
||||||
@@ -204,24 +206,25 @@ public:
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T = std::string, mask_type current_mask = mask,
|
template <typename T = std::string, mask_type current_mask = mask,
|
||||||
std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0, typename Callable>
|
std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0, typename Callable>
|
||||||
auto action(Callable&& handler) const
|
auto action(Callable &&handler) const
|
||||||
-> std::enable_if_t<std::is_invocable_r_v<void, Callable, const T&>,
|
-> std::enable_if_t<std::is_invocable_r_v<void, Callable, const T &>,
|
||||||
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>> {
|
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>> {
|
||||||
static_assert(!std::is_same_v<T, void>, "action<void>(...) is not supported. Use action([] { ... }) instead.");
|
static_assert(!std::is_same_v<T, void>,
|
||||||
|
"action<void>(...) is not supported. Use action([] { ... }) instead.");
|
||||||
|
|
||||||
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
|
||||||
|
|
||||||
next_argument next{*this};
|
next_argument next{*this};
|
||||||
next.m_action = std::make_shared<argument_parser::parametered_action<T>>(
|
next.m_action = std::make_shared<argument_parser::parametered_action<T>>(
|
||||||
std::function<void(const T&)>(std::forward<Callable>(handler)));
|
std::function<void(const T &)>(std::forward<Callable>(handler)));
|
||||||
next.m_value_mode = value_mode::parametered_action;
|
next.m_value_mode = value_mode::parametered_action;
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type current_mask = mask, std::enable_if_t<builder_mask::is_buildable(current_mask), int> = 0>
|
template <mask_type current_mask = mask, std::enable_if_t<builder_mask::is_buildable(current_mask), int> = 0>
|
||||||
auto build(argument_parser::v2::base_parser& parser) const -> void {
|
auto build(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
assert_has_identifier();
|
assert_has_identifier();
|
||||||
|
|
||||||
switch (m_value_mode) {
|
switch (m_value_mode) {
|
||||||
@@ -261,23 +264,19 @@ public:
|
|||||||
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
argument() = default;
|
argument() = default;
|
||||||
|
|
||||||
template<mask_type other_mask, typename other_store_type>
|
template <mask_type other_mask, typename other_store_type>
|
||||||
argument(argument<other_mask, other_store_type> const& other)
|
argument(argument<other_mask, other_store_type> const &other)
|
||||||
: m_short_argument(other.m_short_argument),
|
: m_short_argument(other.m_short_argument), m_long_argument(other.m_long_argument),
|
||||||
m_long_argument(other.m_long_argument),
|
m_positional_name(other.m_positional_name), m_position(other.m_position), m_help_text(other.m_help_text),
|
||||||
m_positional_name(other.m_positional_name),
|
m_required(other.m_required), m_action(other.m_action), m_reference(copy_reference(other.m_reference)),
|
||||||
m_position(other.m_position),
|
|
||||||
m_help_text(other.m_help_text),
|
|
||||||
m_required(other.m_required),
|
|
||||||
m_action(other.m_action),
|
|
||||||
m_reference(copy_reference(other.m_reference)),
|
|
||||||
m_value_mode(other.m_value_mode) {}
|
m_value_mode(other.m_value_mode) {}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
using typed_map = std::unordered_map<v2_flag, typename argument_parser::v2::base_parser::template typed_flag_value<T>>;
|
using typed_map =
|
||||||
|
std::unordered_map<v2_flag, typename argument_parser::v2::base_parser::template 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, argument_parser::v2::base_parser::non_typed_flag_value>;
|
||||||
|
|
||||||
@@ -305,8 +304,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Map>
|
template <typename Map> auto add_common_pairs(Map &pairs) const -> void {
|
||||||
auto add_common_pairs(Map& pairs) const -> void {
|
|
||||||
using namespace argument_parser::v2::flags;
|
using namespace argument_parser::v2::flags;
|
||||||
|
|
||||||
if (is_positional()) {
|
if (is_positional()) {
|
||||||
@@ -331,8 +329,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> auto make_typed_pairs() const -> typed_map<T> {
|
||||||
auto make_typed_pairs() const -> typed_map<T> {
|
|
||||||
typed_map<T> pairs;
|
typed_map<T> pairs;
|
||||||
add_common_pairs(pairs);
|
add_common_pairs(pairs);
|
||||||
return pairs;
|
return pairs;
|
||||||
@@ -344,24 +341,24 @@ private:
|
|||||||
return pairs;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto build_flag(argument_parser::v2::base_parser& parser) const -> void {
|
auto build_flag(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto pairs = make_non_typed_pairs();
|
auto pairs = make_non_typed_pairs();
|
||||||
parser.add_argument(pairs);
|
parser.add_argument(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto build_default_positional(argument_parser::v2::base_parser& parser) const -> void {
|
auto build_default_positional(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto pairs = make_non_typed_pairs();
|
auto pairs = make_non_typed_pairs();
|
||||||
parser.add_argument(pairs);
|
parser.add_argument(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto build_store(argument_parser::v2::base_parser& parser) const -> void {
|
auto build_store(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto pairs = make_typed_pairs<store_type>();
|
auto pairs = make_typed_pairs<store_type>();
|
||||||
parser.template add_argument<store_type>(pairs);
|
parser.template add_argument<store_type>(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
auto key = lookup_key();
|
||||||
|
|
||||||
if (target == nullptr) {
|
if (target == nullptr) {
|
||||||
@@ -372,8 +369,9 @@ private:
|
|||||||
parser.template add_argument<store_type>(pairs);
|
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 = dynamic_cast<argument_parser::parametered_action<store_type> const*>(m_action.get());
|
auto const *typed_action =
|
||||||
|
dynamic_cast<argument_parser::parametered_action<store_type> const *>(m_action.get());
|
||||||
if (typed_action == nullptr) {
|
if (typed_action == nullptr) {
|
||||||
throw std::logic_error("Stored action is not compatible with the requested parameter type.");
|
throw std::logic_error("Stored action is not compatible with the requested parameter type.");
|
||||||
}
|
}
|
||||||
@@ -383,15 +381,16 @@ private:
|
|||||||
parser.template add_argument<store_type>(pairs);
|
parser.template add_argument<store_type>(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto build_nonparametered_action(argument_parser::v2::base_parser& parser) const -> void {
|
auto build_nonparametered_action(argument_parser::v2::base_parser &parser) const -> void {
|
||||||
auto const* nonparametered_action = dynamic_cast<argument_parser::non_parametered_action const*>(m_action.get());
|
auto const *nonparametered_action =
|
||||||
|
dynamic_cast<argument_parser::non_parametered_action const *>(m_action.get());
|
||||||
if (nonparametered_action == nullptr) {
|
if (nonparametered_action == nullptr) {
|
||||||
throw std::logic_error("Stored action is not a non-parametered action.");
|
throw std::logic_error("Stored action is not a non-parametered action.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_positional()) {
|
if (is_positional()) {
|
||||||
auto wrapped_action = argument_parser::helpers::make_parametered_action<std::string>(
|
auto wrapped_action = argument_parser::helpers::make_parametered_action<std::string>(
|
||||||
[action = *nonparametered_action](std::string const&) { action.invoke(); });
|
[action = *nonparametered_action](std::string const &) { action.invoke(); });
|
||||||
auto pairs = make_typed_pairs<std::string>();
|
auto pairs = make_typed_pairs<std::string>();
|
||||||
pairs[argument_parser::v2::flags::Action] = wrapped_action;
|
pairs[argument_parser::v2::flags::Action] = wrapped_action;
|
||||||
parser.template add_argument<std::string>(pairs);
|
parser.template add_argument<std::string>(pairs);
|
||||||
@@ -410,11 +409,10 @@ private:
|
|||||||
std::string m_help_text{};
|
std::string m_help_text{};
|
||||||
bool m_required = false;
|
bool m_required = false;
|
||||||
std::shared_ptr<argument_parser::action_base const> m_action{};
|
std::shared_ptr<argument_parser::action_base const> m_action{};
|
||||||
store_type* m_reference = nullptr;
|
store_type *m_reference = nullptr;
|
||||||
value_mode m_value_mode = value_mode::unresolved;
|
value_mode m_value_mode = value_mode::unresolved;
|
||||||
|
|
||||||
template<typename other_store_type>
|
template <typename other_store_type> static auto copy_reference(other_store_type *reference) -> store_type * {
|
||||||
static auto copy_reference(other_store_type* reference) -> store_type* {
|
|
||||||
if constexpr (std::is_same_v<store_type, other_store_type>) {
|
if constexpr (std::is_same_v<store_type, other_store_type>) {
|
||||||
return reference;
|
return reference;
|
||||||
} else {
|
} else {
|
||||||
@@ -422,62 +420,56 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<mask_type other_mask, typename other_store_type>
|
template <mask_type other_mask, typename other_store_type> friend class argument;
|
||||||
friend class argument;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
namespace assertions {
|
namespace assertions {
|
||||||
struct noop_handler {
|
struct noop_handler {
|
||||||
void operator()() const {}
|
void operator()() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> struct parameter_sink {
|
||||||
struct parameter_sink {
|
void operator()(T const &) const {}
|
||||||
void operator()(T const&) const {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename = void>
|
template <typename T, typename = void> struct can_use_help_text : std::false_type {};
|
||||||
struct can_use_help_text : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
struct can_use_help_text<T, std::void_t<decltype(std::declval<T>().help_text(std::declval<std::string>()))>> : std::true_type {};
|
struct can_use_help_text<T, std::void_t<decltype(std::declval<T>().help_text(std::declval<std::string>()))>>
|
||||||
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct can_use_position : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct can_use_position<T, std::void_t<decltype(std::declval<T>().position(0))>> : std::true_type {};
|
|
||||||
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct can_use_nonparametered_action : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct can_use_nonparametered_action<T, std::void_t<decltype(std::declval<T>().action(noop_handler{}))>> : std::true_type {};
|
|
||||||
|
|
||||||
template<typename T, typename U, typename = void>
|
|
||||||
struct can_use_parametered_action : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T, typename U>
|
|
||||||
struct can_use_parametered_action<T, U, std::void_t<decltype(std::declval<T>().template action<U>(parameter_sink<U>{}))>>
|
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
template<typename T, typename U, typename = void>
|
template <typename T, typename = void> struct can_use_position : std::false_type {};
|
||||||
struct can_use_store : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T, typename U>
|
template <typename T>
|
||||||
|
struct can_use_position<T, std::void_t<decltype(std::declval<T>().position(0))>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename = void> struct can_use_nonparametered_action : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct can_use_nonparametered_action<T, std::void_t<decltype(std::declval<T>().action(noop_handler{}))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename U, typename = void> struct can_use_parametered_action : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct can_use_parametered_action<
|
||||||
|
T, U, std::void_t<decltype(std::declval<T>().template action<U>(parameter_sink<U>{}))>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename U, typename = void> struct can_use_store : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
struct can_use_store<T, U, std::void_t<decltype(std::declval<T>().template store<U>())>> : std::true_type {};
|
struct can_use_store<T, U, std::void_t<decltype(std::declval<T>().template store<U>())>> : std::true_type {};
|
||||||
|
|
||||||
template<typename T, typename = void>
|
template <typename T, typename = void> struct can_use_flag : std::false_type {};
|
||||||
struct can_use_flag : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
struct can_use_flag<T, std::void_t<decltype(std::declval<T>().flag())>> : std::true_type {};
|
struct can_use_flag<T, std::void_t<decltype(std::declval<T>().flag())>> : std::true_type {};
|
||||||
|
|
||||||
template<typename T, typename U, typename = void>
|
template <typename T, typename U, typename = void> struct can_use_reference : std::false_type {};
|
||||||
struct can_use_reference : std::false_type {};
|
|
||||||
|
|
||||||
template<typename T, typename U>
|
template <typename T, typename U>
|
||||||
struct can_use_reference<T, U, std::void_t<decltype(std::declval<T>().reference(std::declval<U&>()))>> : std::true_type {};
|
struct can_use_reference<T, U, std::void_t<decltype(std::declval<T>().reference(std::declval<U &>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
using after_help_text = decltype(argument<>::start().help_text("help"));
|
using after_help_text = decltype(argument<>::start().help_text("help"));
|
||||||
static_assert(!can_use_help_text<after_help_text>::value, "help_text() should be single-use.");
|
static_assert(!can_use_help_text<after_help_text>::value, "help_text() should be single-use.");
|
||||||
@@ -489,7 +481,8 @@ namespace assertions {
|
|||||||
static_assert(!can_use_position<after_position>::value, "position() should be single-use.");
|
static_assert(!can_use_position<after_position>::value, "position() should be single-use.");
|
||||||
|
|
||||||
using after_positional_mode_selection = decltype(argument<>::start().positional("path").store<>());
|
using after_positional_mode_selection = decltype(argument<>::start().positional("path").store<>());
|
||||||
static_assert(!can_use_flag<after_positional_mode_selection>::value, "flag() should not be available for positional arguments.");
|
static_assert(!can_use_flag<after_positional_mode_selection>::value,
|
||||||
|
"flag() should not be available for positional arguments.");
|
||||||
|
|
||||||
using after_nonparametered_action =
|
using after_nonparametered_action =
|
||||||
decltype(argument<>::start().short_argument("v").help_text("verbose").action(noop_handler{}));
|
decltype(argument<>::start().short_argument("v").help_text("verbose").action(noop_handler{}));
|
||||||
@@ -511,7 +504,7 @@ namespace assertions {
|
|||||||
static_assert(!can_use_parametered_action<after_parametered_action, std::string>::value,
|
static_assert(!can_use_parametered_action<after_parametered_action, std::string>::value,
|
||||||
"typed action() should be single-use regardless of the parameter type.");
|
"typed action() should be single-use regardless of the parameter type.");
|
||||||
|
|
||||||
}
|
} // namespace assertions
|
||||||
}
|
} // namespace argument_parser::builder
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace argument_parser {
|
|||||||
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);
|
||||||
};
|
};
|
||||||
}
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,7 +15,16 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace argument_parser::v2 {
|
namespace argument_parser::v2 {
|
||||||
enum class add_argument_flags { ShortArgument, LongArgument, Positional, Position, HelpText, Action, Required, Reference };
|
enum class add_argument_flags {
|
||||||
|
ShortArgument,
|
||||||
|
LongArgument,
|
||||||
|
Positional,
|
||||||
|
Position,
|
||||||
|
HelpText,
|
||||||
|
Action,
|
||||||
|
Required,
|
||||||
|
Reference
|
||||||
|
};
|
||||||
|
|
||||||
namespace flags {
|
namespace flags {
|
||||||
constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument;
|
constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument;
|
||||||
@@ -30,7 +39,7 @@ namespace argument_parser::v2 {
|
|||||||
|
|
||||||
class base_parser : private argument_parser::base_parser {
|
class base_parser : private 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>;
|
||||||
|
|
||||||
template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
|
template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
|
||||||
@@ -153,13 +162,11 @@ namespace argument_parser::v2 {
|
|||||||
|
|
||||||
found_params[extended_add_argument_flags::Action] = true;
|
found_params[extended_add_argument_flags::Action] = true;
|
||||||
if constexpr (!std::is_same_v<T, void>) {
|
if constexpr (!std::is_same_v<T, void>) {
|
||||||
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 {
|
} else {
|
||||||
action = helpers::make_parametered_action<T>([ref](T const& t) {
|
action = helpers::make_parametered_action<T>([ref](T const &t) { *ref = t; }).clone();
|
||||||
*ref = t;
|
|
||||||
}).clone();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error("Reference argument must not be void");
|
throw std::logic_error("Reference argument must not be void");
|
||||||
@@ -187,7 +194,8 @@ namespace argument_parser::v2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base::add_argument(short_arg, long_arg, help_text, *static_cast<ActionType *>(&(*action)), required);
|
base::add_argument(short_arg, long_arg, help_text, *static_cast<ActionType *>(&(*action)),
|
||||||
|
required);
|
||||||
break;
|
break;
|
||||||
case candidate_type::store_other:
|
case candidate_type::store_other:
|
||||||
if (help_text.empty()) {
|
if (help_text.empty()) {
|
||||||
@@ -265,20 +273,17 @@ namespace argument_parser::v2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<T, void>) {
|
if constexpr (!std::is_same_v<T, void>) {
|
||||||
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 {
|
} else {
|
||||||
action = helpers::make_parametered_action<T>([ref](T const& t) {
|
action = helpers::make_parametered_action<T>([ref](T const &t) { *ref = t; }).clone();
|
||||||
*ref = t;
|
|
||||||
}).clone();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::logic_error("Reference argument must not be void");
|
throw std::logic_error("Reference 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 &&
|
||||||
@@ -297,8 +302,8 @@ namespace argument_parser::v2 {
|
|||||||
|
|
||||||
if constexpr (IsTyped) {
|
if constexpr (IsTyped) {
|
||||||
if (action) {
|
if (action) {
|
||||||
base::add_positional_argument<T>(positional_name, help_text,
|
base::add_positional_argument<T>(positional_name, help_text, *static_cast<ActionType *>(&(*action)),
|
||||||
*static_cast<ActionType *>(&(*action)), required, position);
|
required, position);
|
||||||
} else {
|
} else {
|
||||||
base::template add_positional_argument<T>(positional_name, help_text, required, position);
|
base::template add_positional_argument<T>(positional_name, help_text, required, position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace argument_parser::parsing_traits {
|
namespace argument_parser::parsing_traits {
|
||||||
using hint_type = const char*;
|
using hint_type = const char *;
|
||||||
|
|
||||||
template <typename T_> struct parser_trait {
|
template <typename T_> struct parser_trait {
|
||||||
using type = T_;
|
using type = T_;
|
||||||
static T_ parse(const std::string &input);
|
static T_ parse(const std::string &input);
|
||||||
static bool validate(T_ const&);
|
static bool validate(T_ const &);
|
||||||
|
|
||||||
static constexpr hint_type format_hint = "value";
|
static constexpr hint_type format_hint = "value";
|
||||||
static constexpr hint_type purpose_hint = "value";
|
static constexpr hint_type purpose_hint = "value";
|
||||||
@@ -51,16 +51,12 @@ namespace argument_parser::parsing_traits {
|
|||||||
static constexpr hint_type purpose_hint = "double precision floating point number";
|
static constexpr hint_type purpose_hint = "double precision floating point number";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constexpr hint_type comma = ",";
|
constexpr hint_type comma = ",";
|
||||||
template <const hint_type* PtrAddr>
|
template <const hint_type *PtrAddr> struct hint_provider {
|
||||||
struct hint_provider {
|
|
||||||
static constexpr hint_type value = *PtrAddr;
|
static constexpr hint_type value = *PtrAddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Providers>
|
template <typename... Providers> struct joiner {
|
||||||
struct joiner {
|
|
||||||
static constexpr auto get_combined() {
|
static constexpr auto get_combined() {
|
||||||
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
|
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
|
||||||
|
|
||||||
@@ -69,7 +65,8 @@ namespace argument_parser::parsing_traits {
|
|||||||
|
|
||||||
auto append = [&](hint_type s) {
|
auto append = [&](hint_type s) {
|
||||||
std::string_view sv{s};
|
std::string_view sv{s};
|
||||||
for (char c : sv) arr[offset++] = c;
|
for (char c : sv)
|
||||||
|
arr[offset++] = c;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,8 +80,7 @@ namespace argument_parser::parsing_traits {
|
|||||||
static constexpr hint_type value = storage.data();
|
static constexpr hint_type value = storage.data();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Providers>
|
template <typename... Providers> constexpr hint_type concat = joiner<Providers...>::value;
|
||||||
constexpr hint_type concat = joiner<Providers...>::value;
|
|
||||||
|
|
||||||
} // namespace argument_parser::parsing_traits
|
} // namespace argument_parser::parsing_traits
|
||||||
|
|
||||||
|
|||||||
254
src/main.cpp
254
src/main.cpp
@@ -1,254 +0,0 @@
|
|||||||
#include "macros.h"
|
|
||||||
#include "traits.hpp"
|
|
||||||
#include <exception>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <argparse>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <regex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct Point {
|
|
||||||
int x, y;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct argument_parser::parsing_traits::parser_trait<Point> {
|
|
||||||
static Point parse(const std::string &input) {
|
|
||||||
auto comma_pos = input.find(',');
|
|
||||||
if (comma_pos == std::string::npos) {
|
|
||||||
throw std::runtime_error("Invalid Point format. Expected 'x,y'.");
|
|
||||||
}
|
|
||||||
int x = std::stoi(input.substr(0, comma_pos));
|
|
||||||
int y = std::stoi(input.substr(comma_pos + 1));
|
|
||||||
return {x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool validate(Point const& p) {
|
|
||||||
return p.x >= 0 && p.y >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARGPARSE_TRAIT_FORMAT_HINT = "x,y";
|
|
||||||
ARGPARSE_TRAIT_PURPOSE_HINT = "coordinates";
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct argument_parser::parsing_traits::parser_trait<std::regex> {
|
|
||||||
static std::regex parse(const std::string &input) {
|
|
||||||
return std::regex(input);
|
|
||||||
}
|
|
||||||
ARGPARSE_TRAIT_FORMAT_HINT = "regex";
|
|
||||||
ARGPARSE_TRAIT_PURPOSE_HINT = "regular expression";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <> struct argument_parser::parsing_traits::parser_trait<std::vector<std::string>> {
|
|
||||||
static std::vector<std::string> parse(const std::string &input) {
|
|
||||||
std::vector<std::string> result;
|
|
||||||
std::stringstream ss{input};
|
|
||||||
std::string item;
|
|
||||||
while (std::getline(ss, item, ',')) {
|
|
||||||
result.push_back(item);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARGPARSE_TRAIT_FORMAT_HINT = "string,string,string";
|
|
||||||
ARGPARSE_TRAIT_PURPOSE_HINT = "list of strings";
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename VT> struct argument_parser::parsing_traits::parser_trait<std::vector<VT>> {
|
|
||||||
static std::vector<VT> parse(const std::string &input) {
|
|
||||||
std::vector<VT> result;
|
|
||||||
std::stringstream ss{input};
|
|
||||||
std::string item;
|
|
||||||
|
|
||||||
while (std::getline(ss, item, ',')) {
|
|
||||||
result.push_back(argument_parser::parsing_traits::parser_trait<VT>::parse(item));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ARGPARSE_TRAIT_FORMAT_HINT = "VT,VT,VT";
|
|
||||||
ARGPARSE_TRAIT_PURPOSE_HINT = "list of VT";
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
|
|
||||||
&argument_parser::conventions::gnu_argument_convention,
|
|
||||||
&argument_parser::conventions::gnu_equal_argument_convention,
|
|
||||||
// &argument_parser::conventions::windows_argument_convention,
|
|
||||||
// &argument_parser::conventions::windows_equal_argument_convention
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto echo = argument_parser::helpers::make_parametered_action<std::string>(
|
|
||||||
[](std::string const &text) { std::cout << text << std::endl; });
|
|
||||||
|
|
||||||
const auto echo_point = argument_parser::helpers::make_parametered_action<Point>(
|
|
||||||
[](Point const &point) { std::cout << "Point(" << point.x << ", " << point.y << ")" << std::endl; });
|
|
||||||
|
|
||||||
const auto cat = argument_parser::helpers::make_parametered_action<std::string>([](std::string const &file_name) {
|
|
||||||
std::ifstream file(file_name);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
throw std::runtime_error("Could not open file");
|
|
||||||
}
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(file, line)) {
|
|
||||||
std::cout << line << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
auto grep(argument_parser::base_parser const &parser, std::string const &filename, std::regex const &pattern) {
|
|
||||||
if (filename.empty()) {
|
|
||||||
std::cerr << "Missing filename" << std::endl;
|
|
||||||
parser.display_help(conventions);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
std::ifstream file(filename);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
std::cerr << "Could not open file: \"" << filename << '"' << std::endl;
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::string line; std::getline(file, line);) {
|
|
||||||
if (std::regex_search(line, pattern)) {
|
|
||||||
std::cout << line << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_grep(argument_parser::base_parser const &parser) {
|
|
||||||
auto filename = parser.get_optional<std::string>("file");
|
|
||||||
auto pattern = parser.get_optional<std::regex>("grep");
|
|
||||||
|
|
||||||
if (filename && pattern) {
|
|
||||||
grep(parser, filename.value(), pattern.value());
|
|
||||||
} else if (filename) {
|
|
||||||
std::cerr << "Missing grep pattern" << std::endl;
|
|
||||||
parser.display_help(conventions);
|
|
||||||
exit(-1);
|
|
||||||
} else if (pattern) {
|
|
||||||
std::cerr << "Missing filename" << std::endl;
|
|
||||||
parser.display_help(conventions);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_store_point(argument_parser::base_parser const &parser) {
|
|
||||||
auto point = parser.get_optional<Point>("store-point");
|
|
||||||
if (point) {
|
|
||||||
std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int v2Examples() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
argument_parser::v2::parser parser{ false };
|
|
||||||
|
|
||||||
parser.add_argument<std::string>(
|
|
||||||
{{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}});
|
|
||||||
|
|
||||||
parser.add_argument<Point>({{ShortArgument, "ep"}, {LongArgument, "echo-point"}, {Action, echo_point}});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({
|
|
||||||
// stores string for f/file flag
|
|
||||||
{ShortArgument, "f"},
|
|
||||||
{LongArgument, "file"},
|
|
||||||
{HelpText, "File to grep, required only if using grep"},
|
|
||||||
// if no action, falls to store operation with given type.
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.add_argument<std::regex>({
|
|
||||||
// stores string for g/grep flag
|
|
||||||
{ShortArgument, "g"},
|
|
||||||
{LongArgument, "grep"},
|
|
||||||
{HelpText, "Grep pattern, required only if using file"},
|
|
||||||
// same as 'file' flag
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>(
|
|
||||||
{{ShortArgument, "c"}, {LongArgument, "cat"}, {Action, cat}, {HelpText, "Prints the content of the file"}}
|
|
||||||
);
|
|
||||||
|
|
||||||
parser.add_argument<Point>({
|
|
||||||
// { ShortArgument, "sp" }, // now if ShortArgument or LongArgument is missing, it will use it for the other.
|
|
||||||
{LongArgument, "store-point"},
|
|
||||||
{Required, true} // makes this flag required
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({
|
|
||||||
{Positional, "input"},
|
|
||||||
{HelpText, "Input file to process"},
|
|
||||||
{Required, true},
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({
|
|
||||||
{Positional, "output"},
|
|
||||||
{HelpText, "Output file path"},
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.add_argument<std::vector<Point>>({{LongArgument, "points"}, {HelpText, "List of points to store"}});
|
|
||||||
|
|
||||||
parser.on_complete(::run_grep);
|
|
||||||
parser.on_complete(::run_store_point);
|
|
||||||
parser.on_complete([](argument_parser::base_parser const &p) {
|
|
||||||
auto input = p.get_optional<std::string>("input");
|
|
||||||
auto output = p.get_optional<std::string>("output");
|
|
||||||
if (input) {
|
|
||||||
std::cout << "Input: " << input.value() << std::endl;
|
|
||||||
}
|
|
||||||
if (output) {
|
|
||||||
std::cout << "Output: " << output.value() << std::endl;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto unique_copy(std::unique_ptr<std::string> ptr) {
|
|
||||||
std::cout << *ptr << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto unique_reference(std::unique_ptr<std::string> const& ptr) {
|
|
||||||
std::cout << *ptr << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto unique_move(std::unique_ptr<std::string>&& ptr) {
|
|
||||||
std::cout << *ptr << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T return_example(std::function<T()> func) {
|
|
||||||
if constexpr (std::is_same_v<void, T>) {
|
|
||||||
return func();
|
|
||||||
} else {
|
|
||||||
return func();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void log_result(std::function<T()> func) {
|
|
||||||
if constexpr (std::is_same_v<void, T>) {
|
|
||||||
func();
|
|
||||||
} else {
|
|
||||||
std::cout << "result: " << func() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
try {
|
|
||||||
return_example<void>([]{});
|
|
||||||
|
|
||||||
return v2Examples();
|
|
||||||
} catch (std::exception const &e) {
|
|
||||||
std::cout << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -50,7 +50,8 @@ namespace argument_parser::conventions::implementations {
|
|||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg, std::string const &long_arg,
|
std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg,
|
||||||
|
std::string const &long_arg,
|
||||||
bool requires_value) const {
|
bool requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part = "";
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && short_arg != "") {
|
||||||
@@ -129,7 +130,8 @@ 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, bool requires_value) const {
|
std::string const &long_arg,
|
||||||
|
bool requires_value) const {
|
||||||
std::string s_part = "";
|
std::string s_part = "";
|
||||||
if (short_arg != "-" && short_arg != "") {
|
if (short_arg != "-" && short_arg != "") {
|
||||||
s_part += short_prec() + short_arg;
|
s_part += short_prec() + short_arg;
|
||||||
|
|||||||
@@ -36,8 +36,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<std::string>(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);
|
set_program_name(program_name);
|
||||||
@@ -46,5 +45,5 @@ namespace argument_parser {
|
|||||||
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) {
|
||||||
ref_parsed_args() = parsed_arguments;
|
ref_parsed_args() = parsed_arguments;
|
||||||
}
|
}
|
||||||
}
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
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 { before_for; } while(false); \
|
do { \
|
||||||
|
before_for; \
|
||||||
|
} while (false); \
|
||||||
for (int i = 1; i < argc_name; ++i) { \
|
for (int i = 1; i < argc_name; ++i) { \
|
||||||
for_body \
|
for_body \
|
||||||
} \
|
} \
|
||||||
|
|||||||
420
src/test.cpp
420
src/test.cpp
@@ -1,420 +0,0 @@
|
|||||||
#include <argparse>
|
|
||||||
#include <array>
|
|
||||||
#include <cassert>
|
|
||||||
#include <exception>
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
|
|
||||||
&argument_parser::conventions::gnu_argument_convention,
|
|
||||||
&argument_parser::conventions::gnu_equal_argument_convention,
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace v2_test {
|
|
||||||
class fake_parser : public argument_parser::v2::base_parser {
|
|
||||||
public:
|
|
||||||
fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments) {
|
|
||||||
set_program_name(program_name);
|
|
||||||
ref_parsed_args() = std::vector<std::string>(arguments);
|
|
||||||
prepare_help_flag();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace v2_test
|
|
||||||
|
|
||||||
int tests_run = 0;
|
|
||||||
int tests_passed = 0;
|
|
||||||
|
|
||||||
void test_result(const char *name, bool passed) {
|
|
||||||
tests_run++;
|
|
||||||
if (passed) {
|
|
||||||
tests_passed++;
|
|
||||||
std::cout << " [PASS] " << name << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << " [FAIL] " << name << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// V1 Tests (using argument_parser::fake_parser)
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
void test_v1_single_positional_store() {
|
|
||||||
argument_parser::fake_parser parser("test", {"hello"});
|
|
||||||
parser.add_positional_argument<std::string>("greeting", "A greeting", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<std::string>("greeting");
|
|
||||||
test_result("v1: single positional store", val.has_value() && val.value() == "hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_multiple_positionals_ordered() {
|
|
||||||
argument_parser::fake_parser parser("test", {"alpha", "beta", "gamma"});
|
|
||||||
parser.add_positional_argument<std::string>("first", "First arg", false);
|
|
||||||
parser.add_positional_argument<std::string>("second", "Second arg", false);
|
|
||||||
parser.add_positional_argument<std::string>("third", "Third arg", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto first = parser.get_optional<std::string>("first");
|
|
||||||
auto second = parser.get_optional<std::string>("second");
|
|
||||||
auto third = parser.get_optional<std::string>("third");
|
|
||||||
|
|
||||||
bool ok = first.has_value() && first.value() == "alpha" && second.has_value() && second.value() == "beta" &&
|
|
||||||
third.has_value() && third.value() == "gamma";
|
|
||||||
test_result("v1: multiple positionals preserve order", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_with_explicit_position() {
|
|
||||||
argument_parser::fake_parser parser("test", {"first_val", "second_val"});
|
|
||||||
parser.add_positional_argument<std::string>("second", "Second", false, 1);
|
|
||||||
parser.add_positional_argument<std::string>("first", "First", false, 0);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto first = parser.get_optional<std::string>("first");
|
|
||||||
auto second = parser.get_optional<std::string>("second");
|
|
||||||
|
|
||||||
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
|
|
||||||
test_result("v1: explicit position index", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_typed_int() {
|
|
||||||
argument_parser::fake_parser parser("test", {"42"});
|
|
||||||
parser.add_positional_argument<int>("count", "A count", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<int>("count");
|
|
||||||
test_result("v1: positional with int type", val.has_value() && val.value() == 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_with_action() {
|
|
||||||
std::string captured;
|
|
||||||
argument_parser::fake_parser parser("test", {"world"});
|
|
||||||
|
|
||||||
auto action =
|
|
||||||
argument_parser::helpers::make_parametered_action<std::string>([&](std::string const &v) { captured = v; });
|
|
||||||
parser.add_positional_argument<std::string>("name", "A name", action, false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
test_result("v1: positional with action", captured == "world");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_mixed_named_and_positional() {
|
|
||||||
argument_parser::fake_parser parser("test", {"--verbose", "true", "myfile.txt"});
|
|
||||||
parser.add_argument<bool>("v", "verbose", "Verbose mode", false);
|
|
||||||
parser.add_positional_argument<std::string>("file", "Input file", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto verbose = parser.get_optional<bool>("verbose");
|
|
||||||
auto file = parser.get_optional<std::string>("file");
|
|
||||||
|
|
||||||
bool ok = verbose.has_value() && verbose.value() == true && file.has_value() && file.value() == "myfile.txt";
|
|
||||||
test_result("v1: mixed named and positional args", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_after_named() {
|
|
||||||
argument_parser::fake_parser parser("test", {"-n", "5", "output.txt"});
|
|
||||||
parser.add_argument<int>("n", "number", "A number", false);
|
|
||||||
parser.add_positional_argument<std::string>("output", "Output file", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto number = parser.get_optional<int>("number");
|
|
||||||
auto output = parser.get_optional<std::string>("output");
|
|
||||||
|
|
||||||
bool ok = number.has_value() && number.value() == 5 && output.has_value() && output.value() == "output.txt";
|
|
||||||
test_result("v1: positional after named args", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_between_named() {
|
|
||||||
argument_parser::fake_parser parser("test", {"-a", "1", "positional_val", "--beta", "2"});
|
|
||||||
parser.add_argument<int>("a", "alpha", "Alpha", false);
|
|
||||||
parser.add_argument<int>("b", "beta", "Beta", false);
|
|
||||||
parser.add_positional_argument<std::string>("middle", "Middle arg", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto alpha = parser.get_optional<int>("alpha");
|
|
||||||
auto beta = parser.get_optional<int>("beta");
|
|
||||||
auto middle = parser.get_optional<std::string>("middle");
|
|
||||||
|
|
||||||
bool ok = alpha.has_value() && alpha.value() == 1 && beta.has_value() && beta.value() == 2 && middle.has_value() &&
|
|
||||||
middle.value() == "positional_val";
|
|
||||||
test_result("v1: positional between named args", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_double_dash_separator() {
|
|
||||||
argument_parser::fake_parser parser("test", {"--", "-not-a-flag"});
|
|
||||||
parser.add_positional_argument<std::string>("item", "An item", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<std::string>("item");
|
|
||||||
test_result("v1: -- separator treats next as positional", val.has_value() && val.value() == "-not-a-flag");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_double_dash_multiple() {
|
|
||||||
argument_parser::fake_parser parser("test", {"--name", "hello", "--", "--weird", "-x"});
|
|
||||||
parser.add_argument<std::string>("n", "name", "A name", false);
|
|
||||||
parser.add_positional_argument<std::string>("first", "First", false);
|
|
||||||
parser.add_positional_argument<std::string>("second", "Second", false);
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto name = parser.get_optional<std::string>("name");
|
|
||||||
auto first = parser.get_optional<std::string>("first");
|
|
||||||
auto second = parser.get_optional<std::string>("second");
|
|
||||||
|
|
||||||
bool ok = name.has_value() && name.value() == "hello" && first.has_value() && first.value() == "--weird" &&
|
|
||||||
second.has_value() && second.value() == "-x";
|
|
||||||
test_result("v1: -- separator with multiple positionals", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_required_positional_missing() {
|
|
||||||
argument_parser::fake_parser parser("test", {});
|
|
||||||
parser.add_positional_argument<std::string>("file", "A file", true);
|
|
||||||
|
|
||||||
bool threw = false;
|
|
||||||
try {
|
|
||||||
// check_for_required_arguments calls std::exit(1) so we can't easily test it
|
|
||||||
// instead, test that handle_arguments doesn't crash when positionals are provided
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
} catch (...) {
|
|
||||||
threw = true;
|
|
||||||
}
|
|
||||||
// Note: required check calls std::exit(1), so if we get here the arg wasn't required-checked
|
|
||||||
// This test just verifies setup doesn't crash. The exit behavior is tested manually.
|
|
||||||
test_result("v1: required positional setup (no crash)", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_unexpected_positional_throws() {
|
|
||||||
argument_parser::fake_parser parser("test", {"unexpected"});
|
|
||||||
// no positional args defined, but a bare token is provided
|
|
||||||
|
|
||||||
bool threw = false;
|
|
||||||
try {
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
} catch (const std::runtime_error &) {
|
|
||||||
threw = true;
|
|
||||||
}
|
|
||||||
test_result("v1: unexpected positional throws", threw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_duplicate_positional_name_throws() {
|
|
||||||
argument_parser::fake_parser parser("test", {"a", "b"});
|
|
||||||
parser.add_positional_argument<std::string>("file", "A file", false);
|
|
||||||
|
|
||||||
bool threw = false;
|
|
||||||
try {
|
|
||||||
parser.add_positional_argument<std::string>("file", "Duplicate", false);
|
|
||||||
} catch (const std::runtime_error &) {
|
|
||||||
threw = true;
|
|
||||||
}
|
|
||||||
test_result("v1: duplicate positional name throws", threw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v1_positional_on_complete() {
|
|
||||||
std::string captured_file;
|
|
||||||
argument_parser::fake_parser parser("test", {"data.csv"});
|
|
||||||
parser.add_positional_argument<std::string>("file", "Input file", false);
|
|
||||||
parser.on_complete([&](argument_parser::base_parser const &p) {
|
|
||||||
auto val = p.get_optional<std::string>("file");
|
|
||||||
if (val)
|
|
||||||
captured_file = val.value();
|
|
||||||
});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
test_result("v1: positional accessible in on_complete", captured_file == "data.csv");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// V2 Tests (using v2_test::fake_parser)
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
void test_v2_single_positional() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"hello"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "greeting"}, {HelpText, "A greeting"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<std::string>("greeting");
|
|
||||||
test_result("v2: single positional store", val.has_value() && val.value() == "hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_positional_required() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"value"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "arg"}, {Required, true}, {HelpText, "Required arg"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<std::string>("arg");
|
|
||||||
test_result("v2: required positional", val.has_value() && val.value() == "value");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_positional_with_position() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"first_val", "second_val"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "second"}, {Position, 1}, {HelpText, "Second"}});
|
|
||||||
parser.add_argument<std::string>({{Positional, "first"}, {Position, 0}, {HelpText, "First"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto first = parser.get_optional<std::string>("first");
|
|
||||||
auto second = parser.get_optional<std::string>("second");
|
|
||||||
|
|
||||||
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
|
|
||||||
test_result("v2: positional with explicit Position", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_positional_typed_int() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"99"});
|
|
||||||
|
|
||||||
parser.add_argument<int>({{Positional, "count"}, {HelpText, "A count"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<int>("count");
|
|
||||||
test_result("v2: positional with int type", val.has_value() && val.value() == 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_mixed_named_and_positional() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"--output", "out.txt", "input.txt"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{ShortArgument, "o"}, {LongArgument, "output"}, {HelpText, "Output file"}});
|
|
||||||
parser.add_argument<std::string>({{Positional, "input"}, {HelpText, "Input file"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto output = parser.get_optional<std::string>("output");
|
|
||||||
auto input = parser.get_optional<std::string>("input");
|
|
||||||
|
|
||||||
bool ok = output.has_value() && output.value() == "out.txt" && input.has_value() && input.value() == "input.txt";
|
|
||||||
test_result("v2: mixed named and positional", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_positional_with_action() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
std::string captured;
|
|
||||||
v2_test::fake_parser parser("test", {"world"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "name"},
|
|
||||||
{Action, argument_parser::helpers::make_parametered_action<std::string>(
|
|
||||||
[&](std::string const &v) { captured = v; })},
|
|
||||||
{HelpText, "A name"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
test_result("v2: positional with action", captured == "world");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_double_dash_separator() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"--", "--not-a-flag"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "item"}, {HelpText, "An item"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<std::string>("item");
|
|
||||||
test_result("v2: -- separator", val.has_value() && val.value() == "--not-a-flag");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_positional_auto_help_text() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"42"});
|
|
||||||
|
|
||||||
// no HelpText provided — should auto-generate from traits
|
|
||||||
parser.add_argument<int>({{Positional, "count"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto val = parser.get_optional<int>("count");
|
|
||||||
test_result("v2: positional auto help text (no crash)", val.has_value() && val.value() == 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_multiple_positionals_and_named() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
v2_test::fake_parser parser("test", {"-v", "src.txt", "dst.txt"});
|
|
||||||
|
|
||||||
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
|
|
||||||
parser.add_argument<std::string>({{Positional, "source"}, {HelpText, "Source"}});
|
|
||||||
parser.add_argument<std::string>({{Positional, "destination"}, {HelpText, "Destination"}});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
auto verbose = parser.get_optional<bool>("verbose");
|
|
||||||
auto source = parser.get_optional<std::string>("source");
|
|
||||||
auto dest = parser.get_optional<std::string>("destination");
|
|
||||||
|
|
||||||
bool ok = verbose.has_value() && source.has_value() && source.value() == "src.txt" && dest.has_value() &&
|
|
||||||
dest.value() == "dst.txt";
|
|
||||||
test_result("v2: multiple positionals with named flag", ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_v2_on_complete_with_positional() {
|
|
||||||
using namespace argument_parser::v2::flags;
|
|
||||||
std::string captured;
|
|
||||||
v2_test::fake_parser parser("test", {"payload"});
|
|
||||||
|
|
||||||
parser.add_argument<std::string>({{Positional, "data"}, {HelpText, "Data"}});
|
|
||||||
parser.on_complete([&](argument_parser::base_parser const &p) {
|
|
||||||
auto val = p.get_optional<std::string>("data");
|
|
||||||
if (val)
|
|
||||||
captured = val.value();
|
|
||||||
});
|
|
||||||
parser.handle_arguments(conventions);
|
|
||||||
|
|
||||||
test_result("v2: positional accessible in on_complete", captured == "payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Main
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::cout << "=== V1 Positional Argument Tests ===" << std::endl;
|
|
||||||
|
|
||||||
std::array<std::function<void()>, 13> v1Tests {
|
|
||||||
test_v1_single_positional_store,
|
|
||||||
test_v1_multiple_positionals_ordered,
|
|
||||||
test_v1_positional_with_explicit_position,
|
|
||||||
test_v1_positional_typed_int,
|
|
||||||
test_v1_positional_with_action,
|
|
||||||
test_v1_mixed_named_and_positional,
|
|
||||||
test_v1_positional_after_named,
|
|
||||||
test_v1_positional_between_named,
|
|
||||||
test_v1_double_dash_separator,
|
|
||||||
test_v1_double_dash_multiple,
|
|
||||||
test_v1_unexpected_positional_throws,
|
|
||||||
test_v1_duplicate_positional_name_throws,
|
|
||||||
test_v1_positional_on_complete
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto const& test : v1Tests) {
|
|
||||||
try {
|
|
||||||
test();
|
|
||||||
} catch(std::exception const& e) {
|
|
||||||
std::cout << "test failed: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\n=== V2 Positional Argument Tests ===" << std::endl;
|
|
||||||
std::array<std::function<void()>, 10> v2Tests{
|
|
||||||
test_v2_single_positional,
|
|
||||||
test_v2_positional_required,
|
|
||||||
test_v2_positional_with_position,
|
|
||||||
test_v2_positional_typed_int,
|
|
||||||
test_v2_mixed_named_and_positional,
|
|
||||||
test_v2_positional_with_action,
|
|
||||||
test_v2_double_dash_separator,
|
|
||||||
test_v2_positional_auto_help_text,
|
|
||||||
test_v2_multiple_positionals_and_named,
|
|
||||||
test_v2_on_complete_with_positional
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
for (auto const& test : v2Tests) {
|
|
||||||
try {
|
|
||||||
test();
|
|
||||||
} catch(std::exception const& e) {
|
|
||||||
std::cout << "test failed: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\n=== Results: " << tests_passed << "/" << tests_run << " passed ===" << std::endl;
|
|
||||||
return (tests_passed == tests_run) ? 0 : 1;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user