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 a14d57316e69826f82d9eca3581f89c7b1edf35c
parent 1d6c74943aa185bcde7b33da4cf27d8fcf647e95
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Fri, 23 May 2025 23:01:20 +0200

Centralized error reporting

Diffstat:
A include/poafloc/error.hpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M include/poafloc/poafloc.hpp | +++++++++++++ ------------------------------------------------

2 files changed, 73 insertions(+), 48 deletions(-)


diff --git a/ include/poafloc/error.hpp b/ include/poafloc/error.hpp

@@ -0,0 +1,60 @@

#pragma once

#include <format>
#include <stdexcept>

#include <based/enum/enum.hpp>
#include <based/types/types.hpp>
#include <based/utility/forward.hpp>

namespace poafloc
{

#define ENUM_ERROR \
invalid_char, missing_argument, superfluous_argument, unknown_option, \
duplicate_option
BASED_DECLARE_ENUM(error_t, based::bu8, 0, ENUM_ERROR)
BASED_DEFINE_ENUM(error_t, based::bu8, 0, ENUM_ERROR)
#undef ENUM_ERROR

static constexpr const char* error_get_message(error_t::enum_type error)
{
switch (error()) {
case error_t::invalid_char():
return "Invalid char in option: {}";
case error_t::missing_argument():
return "Missing argument for option: {}";
case error_t::superfluous_argument():
return "Option doesn't require an argument: {}";
case error_t::unknown_option():
return "Unknown option: {}";
case error_t::duplicate_option():
return "Duplicate option: {}";
default:
return "poafloc error, should not happen...";
}
}

class runtime_error : public std::runtime_error
{
public:
explicit runtime_error(const std::string& err)
: std::runtime_error(err)
{
}
};

template<error_t::enum_type e>
class error : public runtime_error
{
public:
template<class... Args>
explicit error(Args... args)
: runtime_error(
std::format(error_get_message(e), based::forward<Args>(args)...)
)
{
}
};

} // namespace poafloc

diff --git a/ include/poafloc/poafloc.hpp b/ include/poafloc/poafloc.hpp

@@ -13,6 +13,8 @@

#include <string_view>
#include <vector>

#include "poafloc/error.hpp"

namespace poafloc
{

@@ -141,20 +143,13 @@ struct option_base

static auto convert(char chr)
{
if (!is_valid(chr)) {
invalid_char(chr);
throw error<error_t::invalid_char>(chr);
}

return mapping[static_cast<container_type::size_type>(
static_cast<unsigned char>(chr)
)];
}

[[noreturn]] static void invalid_char(char c)
{
throw std::runtime_error {
std::format("Invalid char in option: {}", c),
};
}
};

class option_short : option_base

@@ -282,23 +277,17 @@ class parser


if (str[1] != '-') {
if (std::size(str) != 2) {
throw std::runtime_error {std::format(
"Short option requires one character: {}", str.substr(1)
)};
continue;
}

const auto opt = str[1];
if (!m_opt_short.set(opt, std::size(m_options))) {
throw std::runtime_error {
std::format("Duplicate short option: {}", opt)
};
throw error<error_t::duplicate_option>(opt);
}
} else {
const auto opt = str.substr(2);
if (!m_opt_long.set(opt, std::size(m_options))) {
throw std::runtime_error {
std::format("Duplicate long option: {}", opt)
};
throw error<error_t::duplicate_option>(opt);
}
}
}

@@ -310,7 +299,7 @@ class parser

{
const auto idx = m_opt_short.get(opt);
if (!idx.has_value()) {
unknown_option(opt);
throw error<error_t::unknown_option>(opt);
}
return m_options[idx.value()];
}

@@ -319,35 +308,11 @@ class parser

{
const auto idx = m_opt_long.get(opt);
if (!idx.has_value()) {
unknown_option(opt);
throw error<error_t::unknown_option>(opt);
}
return m_options[idx.value()];
}

template<class T>
[[noreturn]] static void missing_argument(T opt)
{
throw std::runtime_error {
std::format("Missing argument for option: {}", opt)
};
}

template<class T>
[[noreturn]] static void superfluous_argument(T opt)
{
throw std::runtime_error {
std::format("Option doesn't require an argument: {}", opt)
};
}

template<class T>
[[noreturn]] static void unknown_option(T opt)
{
throw std::runtime_error {
std::format("Unknown option: {}", opt),
};
}

[[noreturn]] static void unhandled_positional(std::string_view arg)
{
throw std::runtime_error {

@@ -424,12 +389,12 @@ class parser

const auto option = get_option(opt);

if (!option.argument()) {
superfluous_argument(opt);
throw error<error_t::superfluous_argument>(opt);
}

const auto arg = mix.substr(equal + 1);
if (arg.empty()) {
missing_argument(opt);
throw error<error_t::missing_argument>(opt);
}

option(record, arg);

@@ -459,7 +424,7 @@ public:

const auto arg_raw = args[arg_idx];

if (arg_raw.size() < 2) {
unknown_option(arg_raw);
throw error<error_t::unknown_option>(arg_raw);
}

if (arg_raw == "--") {

@@ -491,7 +456,7 @@ public:

arg_idx++;
break;
case short_res::missing:
missing_argument(arg);
throw error<error_t::missing_argument>(arg);
break;
}

@@ -515,7 +480,7 @@ public:

arg_idx++;
break;
case long_res::missing:
missing_argument(arg);
throw error<error_t::missing_argument>(arg);
break;
}
}