poafloc

Parser 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

Diffstat:
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));
}
}