poaflocParser Of Arguments For Lines Of Commands |
git clone git://git.dimitrijedobrota.com/poafloc.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 0b0ea66959b7a617db75d272d8e9bed40702705b |
parent | 6565e520e7a3acb45284c2c28afb345758702871 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Tue, 27 May 2025 11:03:07 +0200 |
Positional can have a list at the end
M | include/poafloc/poafloc.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ---------------------- |
M | source/poafloc.cpp | | | ++++++++++++++++ ------------ |
2 files changed, 81 insertions(+), 35 deletions(-)
diff --git a/ include/poafloc/poafloc.hpp b/ include/poafloc/poafloc.hpp
@@ -97,7 +97,7 @@
class argument : public detail::option
using member_type = Type Record::*;
public:
using record_type = Record;
using rec_type = Record;
explicit argument(std::string_view name, member_type member)
: base(
@@ -117,7 +117,7 @@
class direct : public detail::option
using member_type = Type Record::*;
public:
using record_type = Record;
using rec_type = Record;
explicit direct(std::string_view opts, member_type member)
: base(
@@ -146,7 +146,7 @@
class boolean : public detail::option
}
public:
using record_type = Record;
using rec_type = Record;
explicit boolean(std::string_view opts, member_type member)
: base(base::type::boolean, opts, create(member))
@@ -162,7 +162,7 @@
class list : public detail::option
using member_type = Type Record::*;
public:
using record_type = Record;
using rec_type = Record;
explicit list(std::string_view opts, member_type member)
: base(
@@ -188,20 +188,48 @@
struct is_argument<argument<Record, Type>> : based::true_type
template<class T>
concept IsArgument = is_argument<T>::value;
template<class T>
struct is_list : based::false_type
{
};
template<class Record, class Type>
struct is_list<list<Record, Type>> : based::true_type
{
};
template<class T>
concept IsList = is_list<T>::value;
class positional_base : public std::vector<detail::option>
{
using base = std::vector<option>;
protected:
explicit positional_base(auto... args)
template<detail::IsArgument... Args>
explicit positional_base(Args&&... args)
: base(std::initializer_list<option> {
based::forward<decltype(args)>(args)...,
based::forward<Args>(args)...,
})
{
}
template<detail::IsArgument... Args, detail::IsList List>
explicit positional_base(Args&&... args, List&& lst)
: base(std::initializer_list<option> {
based::forward<Args>(args)...,
based::forward<List>(lst),
})
{
}
public:
positional_base() = default;
[[nodiscard]] bool is_list() const
{
return !empty() && back().type() == option::type::list;
}
};
} // namespace detail
@@ -209,15 +237,29 @@
public:
template<class Record>
struct positional : detail::positional_base
{
explicit positional(detail::IsArgument auto... args)
requires(std::same_as<Record, typename decltype(args)::record_type> && ...)
: positional_base(based::forward<decltype(args)>(args)...)
template<detail::IsArgument... Args>
explicit positional(Args&&... args)
requires(std::same_as<Record, typename Args::rec_type> && ...)
: positional_base(based::forward<Args>(args)...)
{
}
template<detail::IsArgument... Args, detail::IsList List>
explicit positional(Args&&... args, List&& list)
requires(std::same_as<Record, typename List::rec_type>)
&& (std::same_as<Record, typename Args::rec_type> && ...)
: positional_base(
based::forward<Args>(args)..., based::forward<List>(list)
)
{
}
};
positional(detail::IsArgument auto arg, detail::IsArgument auto... args)
-> positional<typename decltype(arg)::record_type>;
template<detail::IsArgument Arg, detail::IsArgument... Args>
positional(Arg&& arg, Args&&... args) -> positional<typename Arg::rec_type>;
template<detail::IsArgument... Args, detail::IsList List>
positional(Args&&... args, List&& list) -> positional<typename List::rec_type>;
namespace detail
{
@@ -252,9 +294,10 @@
class group_base : public std::vector<detail::option>
std::string m_name;
protected:
explicit group_base(std::string_view name, detail::IsOption auto... args)
template<detail::IsOption... Opts>
explicit group_base(std::string_view name, Opts&&... opts)
: base(std::initializer_list<option> {
based::forward<decltype(args)>(args)...,
based::forward<Opts>(opts)...,
})
, m_name(name)
{
@@ -271,18 +314,17 @@
public:
template<class Record>
struct group : detail::group_base
{
explicit group(std::string_view name, detail::IsOption auto... args)
requires(std::same_as<Record, typename decltype(args)::record_type> && ...)
: group_base(name, based::forward<decltype(args)>(args)...)
template<detail::IsOption... Opts>
explicit group(std::string_view name, Opts&&... opts)
requires(std::same_as<Record, typename Opts::rec_type> && ...)
: group_base(name, based::forward<Opts>(opts)...)
{
}
};
group(
std::string_view name,
detail::IsOption auto arg,
detail::IsOption auto... args
) -> group<typename decltype(arg)::record_type>;
template<detail::IsOption Opt, detail::IsOption... Opts>
group(std::string_view name, Opt&& opt, Opts&&... opts)
-> group<typename Opt::rec_type>;
namespace detail
{
@@ -338,7 +380,7 @@
class parser_base
{
std::vector<option> m_options;
positional_base m_positional;
positional_base m_pos;
detail::option_short m_opt_short;
detail::option_long m_opt_long;
@@ -367,7 +409,7 @@
protected:
template<class... Groups>
explicit parser_base(positional_base positional, Groups&&... groups)
requires(std::same_as<group_base, Groups> && ...)
: m_positional(std::move(positional))
: m_pos(std::move(positional))
{
m_options.reserve(m_options.size() + (groups.size() + ...));
diff --git a/ source/poafloc.cpp b/ source/poafloc.cpp
@@ -53,7 +53,7 @@
void parser_base::operator()(void* record, int argc, const char** argv)
void parser_base::operator()(void* record, std::span<std::string_view> args)
{
std::size_t arg_idx = 0;
bool terminal = false;
bool is_term = false;
while (arg_idx != std::size(args)) {
const auto arg_raw = args[arg_idx];
@@ -62,12 +62,12 @@
void parser_base::operator()(void* record, std::span<std::string_view> args)
break;
}
if (arg_raw.size() == 1) {
if (std::size(arg_raw) == 1) {
throw error<error_code::unknown_option>("-");
}
if (arg_raw == "--") {
terminal = true;
is_term = true;
++arg_idx;
break;
}
@@ -80,25 +80,29 @@
void parser_base::operator()(void* record, std::span<std::string_view> args)
}
std::size_t count = 0;
for (; arg_idx != std::size(args); ++arg_idx) {
const auto arg = args[arg_idx];
if (!terminal && arg == "--") {
while (arg_idx != std::size(args)) {
const auto arg = args[arg_idx++];
if (!is_term && arg == "--") {
throw error<error_code::invalid_terminal>(arg);
}
if (!terminal && ::is_option(arg)) {
if (!is_term && ::is_option(arg)) {
throw error<error_code::invalid_positional>(arg);
}
if (count == m_positional.size()) {
throw error<error_code::superfluous_positional>(m_positional.size());
if (!m_pos.is_list() && count == std::size(m_pos)) {
throw error<error_code::superfluous_positional>(std::size(m_pos));
}
if (count == std::size(m_pos)) {
count--;
}
m_positional[count++](record, arg);
m_pos[count++](record, arg);
}
if (count != m_positional.size()) {
throw error<error_code::missing_positional>(m_positional.size());
if (count < std::size(m_pos)) {
throw error<error_code::missing_positional>(std::size(m_pos));
}
}