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 | 6751c0563a2a6a1ce35df9a3e16d771980cfb282 |
parent | 11cd944de37c2e4e5a09673d68f317fc2676b22e |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Thu, 5 Jun 2025 05:43:57 +0200 |
Tie in help mechanism
* program name gets propagated while parsing
* help options are parsed
* help options are displayed
* cleaner code
M | example/example.cpp | | | ++++++++++++++++++++++++ -- |
M | include/poafloc/error.hpp | | | ++++++++ --- |
M | include/poafloc/poafloc.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++ ----------------------- |
M | source/help.cpp | | | ++++++++++++++++++++++++ -------------------- |
M | source/option.cpp | | | + - |
M | source/poafloc.cpp | | | ++++++++++++++++++++++++++++++++++++++++++ ------------------- |
M | test/source/parser.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++ ------------------------------------ |
7 files changed, 266 insertions(+), 160 deletions(-)
diff --git a/ example/example.cpp b/ example/example.cpp
@@ -73,6 +73,7 @@
int main()
};
const std::vector<std::string_view> cmd_args {
"example",
"-m1.34",
"--name",
"Hello there!",
@@ -87,8 +88,29 @@
int main()
arguments args;
program.help_long();
program.help_short();
{
try {
const std::vector<std::string_view> help {
"example",
"-?",
};
program(args, help);
} catch (const error<error_code::help>& err) {
(void)err;
}
}
{
try {
const std::vector<std::string_view> usage {
"example",
"--usage",
};
program(args, usage);
} catch (const error<error_code::help>& err) {
(void)err;
}
}
std::cout << args << '\n';
program(args, cmd_args);
diff --git a/ include/poafloc/error.hpp b/ include/poafloc/error.hpp
@@ -12,9 +12,10 @@
namespace poafloc
{
#define ENUM_ERROR \
invalid_option, invalid_positional, invalid_terminal, missing_option, \
missing_argument, missing_positional, superfluous_argument, \
superfluous_positional, unknown_option, duplicate_option
help, empty, invalid_option, invalid_positional, invalid_terminal, \
missing_option, missing_argument, missing_positional, \
superfluous_argument, superfluous_positional, unknown_option, \
duplicate_option
BASED_DECLARE_ENUM(error_code, based::bu8, 0, ENUM_ERROR)
BASED_DEFINE_ENUM(error_code, based::bu8, 0, ENUM_ERROR)
#undef ENUM_ERROR
@@ -22,6 +23,10 @@
BASED_DEFINE_ENUM(error_code, based::bu8, 0, ENUM_ERROR)
static constexpr const char* error_get_message(error_code::enum_type error)
{
switch (error()) {
case error_code::help():
return "Help text was displayed";
case error_code::empty():
return "Option list is empty";
case error_code::invalid_option():
return "Invalid option name: {}";
case error_code::invalid_positional():
diff --git a/ include/poafloc/poafloc.hpp b/ include/poafloc/poafloc.hpp
@@ -1,6 +1,5 @@
#pragma once
#include <cstring>
#include <functional>
#include <initializer_list>
#include <memory>
@@ -43,12 +42,12 @@
public:
};
private:
using func_t = std::function<void(void*, std::string_view)>;
using func_type = std::function<void(void*, std::string_view)>;
using size_type = based::u64;
type m_type;
func_t m_func;
func_type m_func;
based::character m_opt_short;
std::string m_opt_long;
@@ -58,11 +57,14 @@
private:
protected:
// used for args
explicit option(type type, func_t func, std::string_view help = "");
explicit option(type opt_type, func_type func, std::string_view help);
// used for options
explicit option(
type type, std::string_view opts, func_t func, std::string_view help = ""
type opt_type,
std::string_view opts,
func_type func,
std::string_view help
);
template<class Record, class Type, class Member = Type Record::*>
@@ -166,7 +168,7 @@
public:
using rec_type = Record;
explicit direct(
std::string_view opts, member_type member, std::string_view help = ""
std::string_view opts, member_type member, std::string_view help
)
: base(
base::type::direct,
@@ -178,19 +180,27 @@
public:
}
};
template<class Record>
template<class Record, class Type>
class boolean : public detail::option
{
using base = detail::option;
using member_type = bool Record::*;
using member_type = Type Record::*;
static auto create(member_type member)
{
return [member](void* record_raw, std::string_view value)
{
(void)value;
auto* record = static_cast<Record*>(record_raw);
std::invoke(member, record) = true;
if constexpr (std::is_invocable_v<member_type, Record, std::string_view>)
{
std::invoke(member, record, value);
} else if constexpr (std::is_invocable_v<member_type, Record, bool>) {
std::invoke(member, record, true);
} else if constexpr (std::is_assignable_v<Type, std::string_view>) {
std::invoke(member, record) = value;
} else {
std::invoke(member, record) = true;
}
};
}
@@ -198,7 +208,7 @@
public:
using rec_type = Record;
explicit boolean(
std::string_view opts, member_type member, std::string_view help = ""
std::string_view opts, member_type member, std::string_view help
)
: base(base::type::boolean, opts, create(member), help)
{
@@ -216,7 +226,7 @@
public:
using rec_type = Record;
explicit list(
std::string_view opts, member_type member, std::string_view help = ""
std::string_view opts, member_type member, std::string_view help
)
: base(
base::type::list,
@@ -308,8 +318,8 @@
struct is_option<direct<Record, Type>> : based::true_type
{
};
template<class Record>
struct is_option<boolean<Record>> : based::true_type
template<class Record, class Type>
struct is_option<boolean<Record, Type>> : based::true_type
{
};
@@ -454,12 +464,20 @@
class parser_base
using next_t = std::span<const std::string_view>;
next_t hdl_long_opt(void* record, std::string_view arg, next_t next) const;
next_t hdl_short_opts(void* record, std::string_view arg, next_t next) const;
next_t hdl_long_opt(
std::string_view program, void* record, std::string_view arg, next_t next
) const;
next_t hdl_short_opts(
std::string_view program, void* record, std::string_view arg, next_t next
) const;
next_t hdl_short_opt(
void* record, based::character opt, std::string_view rest, next_t next
) const;
void help_usage(std::string_view program) const;
[[nodiscard]] bool help_long(std::string_view program) const;
[[nodiscard]] bool help_short(std::string_view program) const;
protected:
template<class... Groups>
explicit parser_base(Groups&&... groups)
@@ -484,14 +502,24 @@
protected:
m_groups.emplace_back(m_options.size(), group.name());
};
(process(groups), ...);
process(group<parser_base> {
"Informational Options",
boolean {
"? help",
&parser_base::help_long,
"Give this help list",
},
boolean {
"usage",
&parser_base::help_short,
"Give a short usage message",
},
});
}
void operator()(void* record, int argc, const char** argv);
void operator()(void* record, std::span<const std::string_view> args);
void help_usage() const;
void help_long() const;
void help_short() const;
};
} // namespace detail
@@ -522,9 +550,6 @@
struct parser : detail::parser_base
{
}
using parser_base::help_long;
using parser_base::help_short;
void operator()(Record& record, int argc, const char** argv)
{
parser_base::operator()(&record, argc, argv);
diff --git a/ source/help.cpp b/ source/help.cpp
@@ -16,11 +16,11 @@
std::string format_option_long(const option& option)
const auto& opt = option.opt_long();
switch (option.get_type()) {
case option::type::boolean:
return std::format("{}", opt);
return std::format("--{}", opt);
case option::type::list:
return std::format("{}={}...", opt, option.name());
return std::format("--{}={}...", opt, option.name());
default:
return std::format("{}={}", opt, option.name());
return std::format("--{}={}", opt, option.name());
}
}
@@ -29,19 +29,21 @@
std::string format_option_short(const option& option)
const auto& opt = option.opt_short();
switch (option.get_type()) {
case option::type::boolean:
return std::format("{}", opt);
return std::format("-{}", opt);
case option::type::list:
return std::format("{} {}...", opt, option.name());
return std::format("-{} {}...", opt, option.name());
default:
return std::format("{} {}", opt, option.name());
return std::format("-{} {}", opt, option.name());
}
}
} // namespace
void parser_base::help_usage() const
void parser_base::help_usage(std::string_view program) const
{
std::cerr << "Usage: program [OPTIONS]";
std::cerr << "Usage: ";
std::cerr << program;
std::cerr << " [OPTIONS]";
for (const auto& pos : m_pos) {
std::cerr << std::format(" {}", pos.name());
}
@@ -51,9 +53,9 @@
void parser_base::help_usage() const
std::cerr << '\n';
}
void parser_base::help_long() const
bool parser_base::help_long(std::string_view program) const
{
help_usage();
help_usage(program);
auto idx = size_type(0_u);
for (const auto& [end_idx, name] : m_groups) {
@@ -69,9 +71,9 @@
void parser_base::help_long() const
line += std::string(4, ' ');
}
if (opt.has_opt_long()) {
line += std::format(" --{},", format_option_long(opt));
line += " ";
line += format_option_long(opt);
}
line.pop_back(); // get rid of superfluous ','
static constexpr const auto zero = std::size_t {0};
static constexpr const auto mid = std::size_t {30};
@@ -82,9 +84,10 @@
void parser_base::help_long() const
}
std::cerr << '\n';
return true;
}
void parser_base::help_short() const
bool parser_base::help_short(std::string_view program) const
{
std::vector<std::string> opts_short;
std::vector<std::string> opts_long;
@@ -95,12 +98,12 @@
void parser_base::help_short() const
if (opt.get_type() == option::type::boolean) {
flags += opt.opt_short().chr();
} else {
opts_short.emplace_back(std::format("[-{}]", format_option_short(opt)));
opts_short.emplace_back(format_option_short(opt));
}
}
if (opt.has_opt_long()) {
opts_long.emplace_back(std::format("[--{}]", format_option_long(opt)));
opts_long.emplace_back(format_option_long(opt));
}
}
@@ -108,7 +111,7 @@
void parser_base::help_short() const
std::ranges::sort(opts_long);
std::ranges::sort(flags);
static const std::string_view usage = "Usage: ";
static const std::string_view usage = "Usage:";
std::cerr << usage << ' ';
std::string line;
@@ -123,17 +126,17 @@
void parser_base::help_short() const
line += data;
};
line += "program";
line += program;
if (!flags.empty()) {
line += std::format(" [-{}]", flags);
print(std::format("[-{}]", flags));
}
for (const auto& opt : opts_short) {
print(opt);
print(std::format("[{}]", opt));
}
for (const auto& opt : opts_long) {
print(opt);
print(std::format("[{}]", opt));
}
for (const auto& pos : m_pos) {
@@ -145,6 +148,7 @@
void parser_base::help_short() const
std::cerr << "...";
}
std::cerr << '\n' << '\n';
return true;
}
} // namespace poafloc::detail
diff --git a/ source/option.cpp b/ source/option.cpp
@@ -18,7 +18,7 @@
struct short_map
{
constexpr bool operator()(based::character chr) const
{
return based::is_alpha(chr);
return based::is_alpha(chr) || chr == '?';
}
};
diff --git a/ source/poafloc.cpp b/ source/poafloc.cpp
@@ -5,14 +5,14 @@
namespace
{
constexpr bool is_option(std::string_view arg)
constexpr bool is_option_str(std::string_view arg)
{
return arg.starts_with("-");
}
constexpr bool is_next_option(std::span<const std::string_view> args)
{
return args.empty() || is_option(args.front());
return args.empty() || is_option_str(args.front());
}
} // namespace
@@ -20,17 +20,20 @@
constexpr bool is_next_option(std::span<const std::string_view> args)
namespace poafloc::detail
{
option::option(option::type type, func_t func, std::string_view help)
: m_type(type)
option::option(option::type opt_type, func_type func, std::string_view help)
: m_type(opt_type)
, m_func(std::move(func))
, m_name(help)
{
}
option::option(
option::type type, std::string_view opts, func_t func, std::string_view help
option::type opt_type,
std::string_view opts,
func_type func,
std::string_view help
)
: m_type(type)
: m_type(opt_type)
, m_func(std::move(func))
{
auto istr = std::istringstream(std::string(opts));
@@ -48,7 +51,7 @@
option::option(
throw error<error_code::missing_option>();
}
if (type != option::type::boolean) {
if (opt_type != option::type::boolean) {
const auto pos = help.find(' ');
m_name = help.substr(0, pos);
m_message = help.substr(pos + 1);
@@ -79,7 +82,7 @@
void parser_base::process(const option& option)
void parser_base::operator()(void* record, int argc, const char** argv)
{
std::vector<std::string_view> args(
argv + 1, argv + argc // NOLINT(*pointer*)
argv, argv + argc // NOLINT(*pointer*)
);
operator()(record, args);
}
@@ -88,13 +91,18 @@
void parser_base::operator()(
void* record, std::span<const std::string_view> args
)
{
std::size_t arg_idx = 0;
if (args.empty()) {
throw error<error_code::empty>();
}
const auto program = args[0];
std::size_t arg_idx = 1;
bool is_term = false;
while (arg_idx != std::size(args)) {
const auto arg_raw = args[arg_idx];
if (!::is_option(arg_raw)) {
if (!is_option_str(arg_raw)) {
break;
}
@@ -110,8 +118,8 @@
void parser_base::operator()(
const auto next = args.subspan(arg_idx + 1);
const auto res = arg_raw[1] != '-'
? hdl_short_opts(record, arg_raw.substr(1), next)
: hdl_long_opt(record, arg_raw.substr(2), next);
? hdl_short_opts(program, record, arg_raw.substr(1), next)
: hdl_long_opt(program, record, arg_raw.substr(2), next);
arg_idx = std::size(args) - std::size(res);
}
@@ -122,7 +130,7 @@
void parser_base::operator()(
throw error<error_code::invalid_terminal>(arg);
}
if (!is_term && ::is_option(arg)) {
if (!is_term && is_option_str(arg)) {
throw error<error_code::invalid_positional>(arg);
}
@@ -181,19 +189,24 @@
parser_base::next_t parser_base::hdl_short_opt(
}
parser_base::next_t parser_base::hdl_short_opts(
void* record, std::string_view arg, next_t next
std::string_view program, void* record, std::string_view arg, next_t next
) const
{
std::size_t opt_idx = 0;
while (opt_idx < std::size(arg)) {
const auto opt = arg[opt_idx];
const auto option = get_option(opt);
if (opt == '?') {
(void)help_long(program);
throw error<error_code::help>();
}
const auto option = get_option(opt);
if (option.get_type() != option::type::boolean) {
break;
}
option(record, "true");
option(record, program);
opt_idx++;
}
@@ -203,7 +216,7 @@
parser_base::next_t parser_base::hdl_short_opts(
}
parser_base::next_t parser_base::hdl_long_opt(
void* record, std::string_view arg, next_t next
std::string_view program, void* record, std::string_view arg, next_t next
) const
{
const auto equal = arg.find('=');
@@ -225,10 +238,20 @@
parser_base::next_t parser_base::hdl_long_opt(
}
const auto opt = arg;
const auto option = get_option(opt);
if (opt == "help") {
(void)help_long(program);
throw error<error_code::help>();
}
if (opt == "usage") {
(void)help_short(program);
throw error<error_code::help>();
}
const auto option = get_option(opt);
if (option.get_type() == option::type::boolean) {
option(record, "true");
option(record, program);
return next;
}
diff --git a/ test/source/parser.cpp b/ test/source/parser.cpp
@@ -19,6 +19,19 @@
TEST_CASE("invalid", "[poafloc/parser]")
int value;
};
SECTION("empty")
{
auto program = parser<arguments> {
group {
"unnamed",
boolean {"f", &arguments::flag, "NUM something"},
},
};
arguments args = {};
std::vector<std::string_view> cmdline = {};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::empty>);
};
SECTION("short number")
{
auto construct = []()
@@ -26,7 +39,7 @@
TEST_CASE("invalid", "[poafloc/parser]")
return parser<arguments> {
group {
"unnamed",
boolean {"1", &arguments::flag},
boolean {"1", &arguments::flag, "NUM something"},
},
};
};
@@ -40,7 +53,7 @@
TEST_CASE("invalid", "[poafloc/parser]")
return parser<arguments> {
group {
"unnamed",
boolean {"FLAG", &arguments::flag},
boolean {"FLAG", &arguments::flag, "something"},
},
};
};
@@ -54,7 +67,7 @@
TEST_CASE("invalid", "[poafloc/parser]")
return parser<arguments> {
group {
"unnamed",
direct {"1value", &arguments::value},
direct {"1value", &arguments::value, "NUM something"},
},
};
};
@@ -68,8 +81,8 @@
TEST_CASE("invalid", "[poafloc/parser]")
return parser<arguments> {
group {
"unnamed",
boolean {"f flag", &arguments::flag},
direct {"f follow", &arguments::value},
boolean {"f flag", &arguments::flag, "something"},
direct {"f follow", &arguments::value, "NUM something"},
},
};
};
@@ -83,8 +96,8 @@
TEST_CASE("invalid", "[poafloc/parser]")
return parser<arguments> {
group {
"unnamed",
boolean {"f flag", &arguments::flag},
direct {"v flag", &arguments::value},
boolean {"f flag", &arguments::flag, "something"},
direct {"v flag", &arguments::value, "something"},
},
};
};
@@ -102,55 +115,55 @@
TEST_CASE("boolean", "[poafloc/parser]")
auto program = parser<arguments> {
group {
"unnamed",
boolean {"f flag", &arguments::flag},
boolean {"f flag", &arguments::flag, "something"},
},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-f"};
std::vector<std::string_view> cmdline = {"test", "-f"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--flag"};
std::vector<std::string_view> cmdline = {"test", "--flag"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--fl"};
std::vector<std::string_view> cmdline = {"test", "--fl"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u"};
std::vector<std::string_view> cmdline = {"test", "-u"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag == false);
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown"};
std::vector<std::string_view> cmdline = {"test", "--unknown"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag == false);
}
SECTION("long superfluous")
{
std::vector<std::string_view> cmdline = {"--fl=something"};
std::vector<std::string_view> cmdline = {"test", "--fl=something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_argument>);
REQUIRE(args.flag == false);
}
SECTION("long superfluous missing")
{
std::vector<std::string_view> cmdline = {"--fl="};
std::vector<std::string_view> cmdline = {"test", "--fl="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_argument>);
REQUIRE(args.flag == false);
}
@@ -166,118 +179,118 @@
TEST_CASE("direct string", "[poafloc/parser]")
auto program = parser<arguments> {
group {
"unnamed",
direct {"n name", &arguments::name},
direct {"n name", &arguments::name, "NAME something"},
},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-n", "something"};
std::vector<std::string_view> cmdline = {"test", "-n", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("short equal")
{
std::vector<std::string_view> cmdline = {"-n=something"};
std::vector<std::string_view> cmdline = {"test", "-n=something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("short together")
{
std::vector<std::string_view> cmdline = {"-nsomething"};
std::vector<std::string_view> cmdline = {"test", "-nsomething"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--name", "something"};
std::vector<std::string_view> cmdline = {"test", "--name", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--na", "something"};
std::vector<std::string_view> cmdline = {"test", "--na", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long equal")
{
std::vector<std::string_view> cmdline = {"--name=something"};
std::vector<std::string_view> cmdline = {"test", "--name=something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long equal partial")
{
std::vector<std::string_view> cmdline = {"--na=something"};
std::vector<std::string_view> cmdline = {"test", "--na=something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("short missing")
{
std::vector<std::string_view> cmdline = {"-n"};
std::vector<std::string_view> cmdline = {"test", "-n"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("short equal missing")
{
std::vector<std::string_view> cmdline = {"-n="};
std::vector<std::string_view> cmdline = {"test", "-n="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long missing")
{
std::vector<std::string_view> cmdline = {"--name"};
std::vector<std::string_view> cmdline = {"test", "--name"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long partial missing")
{
std::vector<std::string_view> cmdline = {"--na"};
std::vector<std::string_view> cmdline = {"test", "--na"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long equal missing")
{
std::vector<std::string_view> cmdline = {"--name="};
std::vector<std::string_view> cmdline = {"test", "--name="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long equal partial missing")
{
std::vector<std::string_view> cmdline = {"--na="};
std::vector<std::string_view> cmdline = {"test", "--na="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u", "something"};
std::vector<std::string_view> cmdline = {"test", "-u", "something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown", "something"};
std::vector<std::string_view> cmdline = {"test", "--unknown", "something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
SECTION("long equal unknown")
{
std::vector<std::string_view> cmdline = {"--unknown=something"};
std::vector<std::string_view> cmdline = {"test", "--unknown=something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
@@ -293,118 +306,118 @@
TEST_CASE("direct value", "[poafloc/parser]")
auto program = parser<arguments> {
group {
"unnamed",
direct {"v value", &arguments::value},
direct {"v value", &arguments::value, "NUM something"},
},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-v", "135"};
std::vector<std::string_view> cmdline = {"test", "-v", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("short equal")
{
std::vector<std::string_view> cmdline = {"-v=135"};
std::vector<std::string_view> cmdline = {"test", "-v=135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("short together")
{
std::vector<std::string_view> cmdline = {"-v135"};
std::vector<std::string_view> cmdline = {"test", "-v135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--value", "135"};
std::vector<std::string_view> cmdline = {"test", "--value", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--val", "135"};
std::vector<std::string_view> cmdline = {"test", "--val", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long equal")
{
std::vector<std::string_view> cmdline = {"--value=135"};
std::vector<std::string_view> cmdline = {"test", "--value=135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long equal partial")
{
std::vector<std::string_view> cmdline = {"--val=135"};
std::vector<std::string_view> cmdline = {"test", "--val=135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("short missing")
{
std::vector<std::string_view> cmdline = {"-v"};
std::vector<std::string_view> cmdline = {"test", "-v"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("short equal missing")
{
std::vector<std::string_view> cmdline = {"-v="};
std::vector<std::string_view> cmdline = {"test", "-v="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long missing")
{
std::vector<std::string_view> cmdline = {"--value"};
std::vector<std::string_view> cmdline = {"test", "--value"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long partial missing")
{
std::vector<std::string_view> cmdline = {"--val"};
std::vector<std::string_view> cmdline = {"test", "--val"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long equal missing")
{
std::vector<std::string_view> cmdline = {"--value="};
std::vector<std::string_view> cmdline = {"test", "--value="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long equal partial missing")
{
std::vector<std::string_view> cmdline = {"--val="};
std::vector<std::string_view> cmdline = {"test", "--val="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u", "135"};
std::vector<std::string_view> cmdline = {"test", "-u", "135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown", "135"};
std::vector<std::string_view> cmdline = {"test", "--unknown", "135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
SECTION("long equal unknown")
{
std::vector<std::string_view> cmdline = {"--unknown=135"};
std::vector<std::string_view> cmdline = {"test", "--unknown=135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
@@ -421,18 +434,18 @@
TEST_CASE("list", "[poafloc/parser]")
auto program = parser<arguments> {group {
"unnamed",
list {"l list", &arguments::add},
list {"l list", &arguments::add, "NAME something"},
}};
SECTION("short empty")
{
std::vector<std::string_view> cmdline = {"-l"};
std::vector<std::string_view> cmdline = {"test", "-l"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
}
SECTION("short one")
{
std::vector<std::string_view> cmdline = {"-l", "one"};
std::vector<std::string_view> cmdline = {"test", "-l", "one"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 1);
REQUIRE(args.list[0] == "one");
@@ -440,7 +453,7 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("short two")
{
std::vector<std::string_view> cmdline = {"-l", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "-l", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 2);
REQUIRE(args.list[0] == "one");
@@ -449,7 +462,9 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("short three")
{
std::vector<std::string_view> cmdline = {"-l", "one", "two", "three"};
std::vector<std::string_view> cmdline = {
"test", "-l", "one", "two", "three"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 3);
REQUIRE(args.list[0] == "one");
@@ -459,13 +474,13 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("short terminal")
{
std::vector<std::string_view> cmdline = {"-l", "--"};
std::vector<std::string_view> cmdline = {"test", "-l", "--"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
}
SECTION("short terminal one")
{
std::vector<std::string_view> cmdline = {"-l", "one", "--"};
std::vector<std::string_view> cmdline = {"test", "-l", "one", "--"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 1);
REQUIRE(args.list[0] == "one");
@@ -473,7 +488,7 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("short terminal two")
{
std::vector<std::string_view> cmdline = {"-l", "one", "two", "--"};
std::vector<std::string_view> cmdline = {"test", "-l", "one", "two", "--"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 2);
REQUIRE(args.list[0] == "one");
@@ -482,7 +497,9 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("short terminal three")
{
std::vector<std::string_view> cmdline = {"-l", "one", "two", "three", "--"};
std::vector<std::string_view> cmdline = {
"test", "-l", "one", "two", "three", "--"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 3);
REQUIRE(args.list[0] == "one");
@@ -492,13 +509,13 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long empty")
{
std::vector<std::string_view> cmdline = {"--list"};
std::vector<std::string_view> cmdline = {"test", "--list"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
}
SECTION("long one")
{
std::vector<std::string_view> cmdline = {"--list", "one"};
std::vector<std::string_view> cmdline = {"test", "--list", "one"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 1);
REQUIRE(args.list[0] == "one");
@@ -506,7 +523,7 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long two")
{
std::vector<std::string_view> cmdline = {"--list", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "--list", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 2);
REQUIRE(args.list[0] == "one");
@@ -515,7 +532,9 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long three")
{
std::vector<std::string_view> cmdline = {"--list", "one", "two", "three"};
std::vector<std::string_view> cmdline = {
"test", "--list", "one", "two", "three"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 3);
REQUIRE(args.list[0] == "one");
@@ -525,13 +544,13 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long terminal")
{
std::vector<std::string_view> cmdline = {"--list", "--"};
std::vector<std::string_view> cmdline = {"test", "--list", "--"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
}
SECTION("long terminal one")
{
std::vector<std::string_view> cmdline = {"--list", "one", "--"};
std::vector<std::string_view> cmdline = {"test", "--list", "one", "--"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 1);
REQUIRE(args.list[0] == "one");
@@ -539,7 +558,9 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long terminal two")
{
std::vector<std::string_view> cmdline = {"--list", "one", "two", "--"};
std::vector<std::string_view> cmdline = {
"test", "--list", "one", "two", "--"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 2);
REQUIRE(args.list[0] == "one");
@@ -549,7 +570,7 @@
TEST_CASE("list", "[poafloc/parser]")
SECTION("long terminal three")
{
std::vector<std::string_view> cmdline = {
"--list", "one", "two", "three", "--"
"test", "--list", "one", "two", "three", "--"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 3);
@@ -576,27 +597,29 @@
TEST_CASE("positional", "[poafloc/parser]")
},
group {
"unnamed",
boolean {"f flag", &arguments::flag},
direct {"v value", &arguments::value},
boolean {"f flag", &arguments::flag, "something"},
direct {"v value", &arguments::value, "NUM something"},
}
};
SECTION("empty")
{
std::vector<std::string_view> cmdline = {};
std::vector<std::string_view> cmdline = {
"test",
};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_positional>);
}
SECTION("one")
{
std::vector<std::string_view> cmdline = {"one"};
std::vector<std::string_view> cmdline = {"test", "one"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_positional>);
REQUIRE(args.one == "one");
}
SECTION("two")
{
std::vector<std::string_view> cmdline = {"one", "two"};
std::vector<std::string_view> cmdline = {"test", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.one == "one");
REQUIRE(args.two == "two");
@@ -604,7 +627,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("three")
{
std::vector<std::string_view> cmdline = {"one", "two", "three"};
std::vector<std::string_view> cmdline = {"test", "one", "two", "three"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_positional>);
REQUIRE(args.one == "one");
REQUIRE(args.two == "two");
@@ -612,7 +635,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("flag short")
{
std::vector<std::string_view> cmdline = {"-f", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "-f", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
REQUIRE(args.one == "one");
@@ -621,7 +644,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("flag long")
{
std::vector<std::string_view> cmdline = {"--flag", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "--flag", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
REQUIRE(args.one == "one");
@@ -630,7 +653,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("value short")
{
std::vector<std::string_view> cmdline = {"-v", "135", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "-v", "135", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
REQUIRE(args.one == "one");
@@ -639,7 +662,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("value short together")
{
std::vector<std::string_view> cmdline = {"-v135", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "-v135", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
REQUIRE(args.one == "one");
@@ -648,7 +671,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("value short together")
{
std::vector<std::string_view> cmdline = {"-v=135", "one", "two"};
std::vector<std::string_view> cmdline = {"test", "-v=135", "one", "two"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
REQUIRE(args.one == "one");
@@ -657,7 +680,9 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("value long")
{
std::vector<std::string_view> cmdline = {"--value", "135", "one", "two"};
std::vector<std::string_view> cmdline = {
"test", "--value", "135", "one", "two"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
REQUIRE(args.one == "one");
@@ -666,7 +691,9 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("value long equal")
{
std::vector<std::string_view> cmdline = {"--value=135", "one", "two"};
std::vector<std::string_view> cmdline = {
"test", "--value=135", "one", "two"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
REQUIRE(args.one == "one");
@@ -675,7 +702,7 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("flag short terminal")
{
std::vector<std::string_view> cmdline = {"--", "one", "-f", "two"};
std::vector<std::string_view> cmdline = {"test", "--", "one", "-f", "two"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_positional>);
REQUIRE(args.one == "one");
REQUIRE(args.two == "-f");
@@ -683,19 +710,19 @@
TEST_CASE("positional", "[poafloc/parser]")
SECTION("invalid terminal")
{
std::vector<std::string_view> cmdline = {"one", "--", "-f", "two"};
std::vector<std::string_view> cmdline = {"test", "one", "--", "-f", "two"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::invalid_terminal>);
}
SECTION("flag short non-terminal")
{
std::vector<std::string_view> cmdline = {"one", "-f", "two"};
std::vector<std::string_view> cmdline = {"test", "one", "-f", "two"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::invalid_positional>);
}
SECTION("flag long non-terminal")
{
std::vector<std::string_view> cmdline = {"one", "--flag", "two"};
std::vector<std::string_view> cmdline = {"test", "one", "--flag", "two"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::invalid_positional>);
}
}
@@ -713,16 +740,16 @@
TEST_CASE("multiple", "[poafloc/parser]")
auto program = parser<arguments> {
group {
"unnamed",
boolean {"f flag1", &arguments::flag1},
boolean {"F flag2", &arguments::flag2},
direct {"v value1", &arguments::value1},
direct {"V value2", &arguments::value2},
boolean {"f flag1", &arguments::flag1, "something"},
boolean {"F flag2", &arguments::flag2, "something"},
direct {"v value1", &arguments::value1, "NUM something"},
direct {"V value2", &arguments::value2, "NUM something"},
},
};
SECTION("valid")
{
std::vector<std::string_view> cmdline = {"--flag1", "--flag2"};
std::vector<std::string_view> cmdline = {"test", "--flag1", "--flag2"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == true);
@@ -732,7 +759,7 @@
TEST_CASE("multiple", "[poafloc/parser]")
SECTION("partial overlap")
{
std::vector<std::string_view> cmdline = {"--fla", "--fla"};
std::vector<std::string_view> cmdline = {"test", "--fla", "--fla"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag1 == false);
REQUIRE(args.flag2 == false);
@@ -742,7 +769,7 @@
TEST_CASE("multiple", "[poafloc/parser]")
SECTION("together")
{
std::vector<std::string_view> cmdline = {"-fvF"};
std::vector<std::string_view> cmdline = {"test", "-fvF"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == false);
@@ -752,7 +779,7 @@
TEST_CASE("multiple", "[poafloc/parser]")
SECTION("together equal")
{
std::vector<std::string_view> cmdline = {"-fv=F"};
std::vector<std::string_view> cmdline = {"test", "-fv=F"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == false);
@@ -762,7 +789,7 @@
TEST_CASE("multiple", "[poafloc/parser]")
SECTION("together next")
{
std::vector<std::string_view> cmdline = {"-fv", "F"};
std::vector<std::string_view> cmdline = {"test", "-fv", "F"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == false);