20#include <system_error>
58 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
63 [[nodiscard]]
constexpr auto have_value() const noexcept ->
bool {
68 [[nodiscard]]
constexpr auto has_value() const noexcept ->
bool {
73 [[nodiscard]]
constexpr auto has_error() const noexcept ->
bool {
78 [[nodiscard]]
constexpr auto is_ignored() const noexcept ->
bool {
91 return {.state =
State::ok, .value = std::forward<U>(v)};
133 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
138 [[nodiscard]]
constexpr auto have_value() const noexcept ->
bool {
143 [[nodiscard]]
constexpr auto has_value() const noexcept ->
bool {
148 [[nodiscard]]
constexpr auto has_error() const noexcept ->
bool {
153 [[nodiscard]]
constexpr auto is_ignored() const noexcept ->
bool {
245template <
class T =
void>
254 std::reference_wrapper<T>
298auto to_chars_string(T
value) -> std::string {
299 std::array<char, 128> buffer{};
300 const auto [ptr, ec] = [&] {
301 if constexpr (std::floating_point<T>) {
302 return std::to_chars(buffer.data(), buffer.data() + buffer.size(),
value,
303 std::chars_format::general);
305 return std::to_chars(buffer.data(), buffer.data() + buffer.size(),
value);
308 if (ec != std::errc{})
return "<value>";
309 return std::string(buffer.data(), ptr);
314auto to_error_subject(
const T&
value) -> std::string {
315 using U = std::remove_cvref_t<T>;
317 if constexpr (std::same_as<U, std::string>) {
319 }
else if constexpr (std::same_as<U, std::string_view>) {
320 return std::string(
value);
321 }
else if constexpr (std::same_as<U, const char*>) {
322 return value ==
nullptr ? std::string{} : std::string(
value);
323 }
else if constexpr (std::is_same_v<U, bool>) {
324 return value ?
"true" :
"false";
325 }
else if constexpr (std::integral<U> || std::floating_point<U>) {
326 return to_chars_string(
value);
327 }
else if constexpr (std::same_as<U, std::filesystem::path>) {
328 return value.string();
329 }
else if constexpr (
requires(std::ostream& os,
const U& v) { os << v; }) {
330 std::ostringstream out;
339inline auto invalid_value_error(
ErrorKind kind, std::size_t index,
340 std::string subject, std::string detail = {})
345 .position =
static_cast<int>(index),
346 .subject = std::move(subject),
347 .detail = std::move(detail),
352inline auto duplicate_argument_error(std::size_t index, std::string subject,
353 std::string detail = {}) -> ParseError {
357 .position =
static_cast<int>(index),
358 .subject = std::move(subject),
359 .detail = std::move(detail),
364inline auto validation_failed_error(std::size_t index, std::string subject,
365 std::string detail) -> ParseError {
369 .position =
static_cast<int>(index),
370 .subject = std::move(subject),
371 .detail = std::move(detail),
376inline auto invalid_choice_error(std::size_t index, std::string subject,
377 std::string detail = {}) -> ParseError {
381 .position =
static_cast<int>(index),
382 .subject = std::move(subject),
383 .detail = std::move(detail),
388inline auto is_blank(std::string_view text) ->
bool {
389 return std::ranges::all_of(
390 text, [](
unsigned char ch) ->
bool {
return std::isspace(ch) != 0; });
394using decay_t = std::remove_cvref_t<T>;
397struct HelpRequested {
402struct IsHelpRequested : std::false_type {};
405struct IsHelpRequested<HelpRequested<T>> : std::true_type {};
408concept PairLike =
requires {
409 typename std::tuple_size<decay_t<T>>::type;
410 requires std::tuple_size_v<decay_t<T>> == 2;
414concept StringLike = std::same_as<decay_t<T>, std::string> ||
415 std::same_as<decay_t<T>, std::string_view>;
443template <
auto... Fns>
446 std::remove_cvref_t<
decltype(Fns)>::template accepts_input<void>
447 } -> std::convertible_to<bool>;
448 typename std::remove_cvref_t<
decltype(Fns)>::template storage_type<void>;
451 template <
class Arg,
class Result>
455 []<
auto FnHead,
auto... FnTail>(
458 using FinalIdentity = std::remove_cvref_t<decltype(std::get<2>(
464 auto next = FnHead(ctx, std::move(input));
465 if constexpr (
sizeof...(FnTail) == 0) {
470 }.template operator()<Fns...>(ctx, std::move(input));
473 template <
class Arg,
class Result>
477 return []<
auto FnHead,
auto... FnTail>(
481 using FinalIdentity = std::remove_cvref_t<decltype(std::get<2>(
487 auto next = FnHead(ctx, std::move(input));
488 if constexpr (
sizeof...(FnTail) == 0) {
493 }.template operator()<Fns...>(ctx, std::move(input));
496 template <
bool EnableTailCheck,
class Prev,
auto FnHead,
auto... FnTail>
500 (
sizeof...(FnTail) != 0 ||
501 !std::same_as<
typename std::remove_cvref_t<
502 decltype(FnHead)>::template storage_type<Prev>,
504 "The last action must have a non-void storage type");
506 using Head = std::remove_cvref_t<
decltype(FnHead)>;
507 if constexpr (
sizeof...(FnTail) == 0) {
508 return std::make_tuple(
509 Head::template accepts_input<Prev>,
510 std::type_identity<
typename Head::template storage_type<Prev>>{},
511 std::type_identity<typename Head::template after_type<Prev>>{});
513 if constexpr (!Head::template accepts_input<Prev>) {
514 static_assert(Head::template accepts_input<Prev>,
515 "Action step does not accept the previous step's output");
516 return std::make_tuple(
false, std::type_identity<void>{},
517 std::type_identity<void>{});
519 using Next =
typename Head::template after_type<Prev>;
526 template <
auto First,
auto...>
528 using type = std::remove_cvref_t<
decltype(First)>;
533 Fns...>::type::template accepts_input<std::optional<std::string_view>>;
536 using FirstFn =
typename FirstFnType<Fns...>::type;
537 if constexpr (FirstFn::template accepts_input<
538 std::optional<std::string_view>>) {
550 template <
class Arg,
class Result>
557 return std::make_tuple(
true, std::type_identity<std::monostate>{},
558 std::type_identity<std::monostate>{});
562template <
auto Fn,
auto... Fns>
564 return Action<Fns..., Fn>{};
587template <
class Step,
class Input>
616template <
class Step,
class Input>
619 typename std::invoke_result_t<const Step&, ActionCtx<void>,
646template <std::
integral T>
648 template <
class Input>
650 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
651 std::same_as<std::remove_cvref_t<Input>, std::string>;
653 template <
class Input>
656 template <
class Prev>
662 if (!input.have_value())
return fail<T>(input.error);
664 auto r = std::from_chars(input.value.data(),
665 input.value.data() + input.value.size(), result);
667 if (r.ec == std::errc::invalid_argument) {
671 if (r.ec == std::errc::result_out_of_range) {
675 .position =
static_cast<int>(ctx.index),
676 .subject = std::string(input.value),
679 if (r.ptr != input.value.data() + input.value.size()) {
682 "unexpected trailing characters"));
698template <std::
floating_po
int T>
700 template <
class Input>
702 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
703 std::same_as<std::remove_cvref_t<Input>, std::string>;
705 template <
class Input>
708 template <
class Prev>
714 if (!input.have_value())
return fail<T>(input.error);
716 auto r = std::from_chars(input.value.data(),
717 input.value.data() + input.value.size(), result);
719 if (r.ec == std::errc::invalid_argument) {
723 if (r.ec == std::errc::result_out_of_range) {
727 .position =
static_cast<int>(ctx.index),
728 .subject = std::string(input.value),
731 if (r.ptr != input.value.data() + input.value.size()) {
734 "unexpected trailing characters"));
743 template <
class Input>
745 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
746 std::same_as<std::remove_cvref_t<Input>, std::string>;
748 template <
class Input>
751 template <
class Prev>
758 return ok(std::string(input.value));
769 template <
class Input>
771 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
772 std::same_as<std::remove_cvref_t<Input>, std::string>;
774 template <
class Input>
777 template <
class Prev>
783 if (!input.have_value())
return fail<bool>(input.error);
784 const std::string_view val = input.value;
785 if (val ==
"true" || val ==
"1") {
788 if (val ==
"false" || val ==
"0") {
793 "expected one of: true, false, 1, 0"));
800 template <
class Input>
802 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
803 std::same_as<std::remove_cvref_t<Input>, std::string>;
805 template <
class Input>
808 template <
class Prev>
814 return ok(std::filesystem::path{input.value});
826 template <
class Input>
829 template <
class Input>
832 template <
class Prev>
839 auto path = std::filesystem::path{input.value};
840 if (!std::filesystem::exists(
path) ||
841 !std::filesystem::is_regular_file(
path)) {
845 "expected an existing regular file"));
847 return ok(std::move(
path));
859 template <
class Input>
862 template <
class Input>
865 template <
class Prev>
872 auto path = std::filesystem::path{input.value};
873 if (!std::filesystem::exists(
path) || !std::filesystem::is_directory(
path)) {
877 "expected an existing directory"));
879 return ok(std::move(
path));
894template <
class T, auto Mapper>
896 template <
class Input>
899 template <
class Input>
902 template <
class Prev>
908 if (!input.have_value())
return fail<T>(input.error);
909 if (
auto value = Mapper(input.value);
value.has_value()) {
913 detail::invalid_choice_error(ctx.index, std::string(input.value)));
917template <
class T, auto Mapper>
920template <std::
integral T>
922template <std::
floating_po
int T>
924template <
class T, auto Mapper>
926template <
class T, auto Mapper>
949template <StringLiteral Prefix>
951 template <
class Input>
953 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
954 std::same_as<std::remove_cvref_t<Input>, std::string>;
956 template <
class Input>
959 template <
class Prev>
966 constexpr std::string_view prefix = Prefix.view();
967 const std::string_view val = input.value;
968 if (val.starts_with(prefix)) {
976template <StringLiteral Prefix>
997template <StringLiteral Value>
999 template <
class Input>
1001 std::same_as<std::remove_cvref_t<Input>, std::optional<std::string_view>>;
1003 template <
class Input>
1006 template <
class Prev>
1010 ActionResult<std::optional<std::string_view>> input)
const
1013 if (input.value.has_value()) {
1015 return ok(std::string_view(*input.value));
1018 if (ctx.nargs == 0) {
1020 return ok(Value.view());
1027template <StringLiteral Value>
1053template <auto MinValue>
1055 template <
class Prev>
1057 std::totally_ordered<std::remove_cvref_t<Prev>>;
1059 template <
class Prev>
1062 template <
class Prev>
1068 if (!input.have_value())
return input;
1069 if (input.value < MinValue) {
1071 ctx.index, detail::to_error_subject(input.value),
1072 "value must be >= " + detail::to_error_subject(MinValue)));
1083template <auto MaxValue>
1085 template <
class Prev>
1087 std::totally_ordered<std::remove_cvref_t<Prev>>;
1089 template <
class Prev>
1092 template <
class Prev>
1098 if (!input.have_value())
return input;
1099 if (input.value > MaxValue) {
1101 ctx.index, detail::to_error_subject(input.value),
1102 "value must be <= " + detail::to_error_subject(MaxValue)));
1115template <auto MinValue, auto MaxValue>
1116 requires std::is_same_v<
decltype(MinValue),
decltype(MaxValue)>
1118 template <
class Prev>
1120 std::totally_ordered<std::remove_cvref_t<Prev>>;
1122 template <
class Prev>
1125 template <
class Prev>
1131 if (!input.have_value())
return input;
1132 if (input.value < MinValue || input.value > MaxValue) {
1134 ctx.index, detail::to_error_subject(input.value),
1135 "value must be between " + detail::to_error_subject(MinValue) +
1136 " and " + detail::to_error_subject(MaxValue)));
1144 template <
class Prev>
1146 std::totally_ordered<detail::decay_t<Prev>>;
1148 template <
class Prev>
1151 template <
class Prev>
1157 if (!input.have_value())
return input;
1158 if (!(input.value > 0)) {
1160 ctx.index, detail::to_error_subject(input.value),
1161 "value must be positive"));
1169 template <
class Prev>
1171 std::totally_ordered<detail::decay_t<Prev>>;
1173 template <
class Prev>
1176 template <
class Prev>
1182 if (!input.have_value())
return input;
1183 if (input.value < 0) {
1185 ctx.index, detail::to_error_subject(input.value),
1186 "value must be non-negative"));
1195 template <
class Prev>
1197 requires(
const detail::decay_t<Prev>&
value) {
value.empty(); };
1199 template <
class Prev>
1202 template <
class Prev>
1208 if (!input.have_value())
return input;
1209 if (input.value.empty()) {
1211 ctx.index, detail::to_error_subject(input.value),
1212 "value must not be empty"));
1220 template <
class Prev>
1222 detail::StringLike<Prev> ||
1223 std::same_as<detail::decay_t<Prev>,
const char*>;
1225 template <
class Prev>
1228 template <
class Prev>
1234 if (!input.have_value())
return input;
1235 std::string_view text = input.value;
1236 if (detail::is_blank(text)) {
1238 ctx.index, std::string(text),
"value must not be blank"));
1251template <
auto... Allowed>
1253 template <
class Prev>
1255 ((std::same_as<detail::decay_t<Prev>,
1256 detail::decay_t<
decltype(Allowed)>>) &&
1259 template <
class Prev>
1262 template <
class Prev>
1268 if (!input.have_value())
return input;
1269 if (((input.value == Allowed) || ...)) {
1273 ctx.index, detail::to_error_subject(input.value),
1274 "value was not one of the allowed choices"));
1287template <StringLiteral Pattern>
1289 template <
class Prev>
1292 template <
class Prev>
1295 template <
class Prev>
1301 if (!input.have_value())
return input;
1302 const auto regex = std::regex{std::string(Pattern.view())};
1303 if (!std::regex_match(input.value.begin(), input.value.end(), regex)) {
1305 ctx.index, detail::to_error_subject(input.value),
1306 "value must match " + std::string(Pattern.view())));
1314 template <
class Prev>
1316 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1318 template <
class Prev>
1321 template <
class Prev>
1327 if (!input.have_value())
return input;
1328 if (!std::filesystem::exists(input.value)) {
1330 ctx.index, input.value.string(),
"path does not exist"));
1338 template <
class Prev>
1340 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1342 template <
class Prev>
1345 template <
class Prev>
1351 if (!input.have_value())
return input;
1352 if (!std::filesystem::is_regular_file(input.value)) {
1354 ctx.index, input.value.string(),
"path is not a regular file"));
1362 template <
class Prev>
1364 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1366 template <
class Prev>
1369 template <
class Prev>
1375 if (!input.have_value())
return input;
1376 if (!std::filesystem::is_directory(input.value)) {
1378 ctx.index, input.value.string(),
"path is not a directory"));
1387 template <
class Prev>
1389 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1391 template <
class Prev>
1394 template <
class Prev>
1400 if (!input.have_value())
return input;
1401 const auto parent = input.value.parent_path();
1402 if (!parent.empty() && !std::filesystem::exists(parent)) {
1404 ctx.index, input.value.string(),
"parent directory does not exist"));
1420 template <
class Prev>
1422 requires(
const detail::decay_t<Prev>&
value) {
1423 { Pred(
value) } -> std::convertible_to<bool>;
1426 template <
class Prev>
1429 template <
class Prev>
1435 if (!input.have_value())
return input;
1436 if (!Pred(input.value)) {
1438 ctx.index, detail::to_error_subject(input.value),
1439 "predicate rejected value"));
1445template <auto MinValue>
1447template <auto MaxValue>
1449template <auto MinValue, auto MaxValue>
1451template <
auto... Allowed>
1453template <StringLiteral Pattern>
1485 template <
class Prev>
1488 template <
class Prev>
1491 template <
class Prev>
1497 if (!input.have_value())
return fail<void>(input.error);
1498 ctx.arg.get() =
true;
1505 template <
class Prev>
1508 template <
class Prev>
1511 template <
class Prev>
1517 if (!input.have_value())
return fail<void>(input.error);
1518 ctx.arg.get() =
false;
1525 template <
class Prev>
1528 template <
class Prev>
1531 template <
class Prev>
1537 if (!input.have_value())
return fail<void>(input.error);
1538 ctx.arg.get() = !ctx.arg.get();
1545 template <
class Prev>
1548 template <
class Prev>
1551 template <
class Prev>
1557 if (!input.have_value())
return fail<void>(input.error);
1571 template <
class Prev>
1573 !std::same_as<detail::decay_t<Prev>,
void>;
1575 template <
class Prev>
1578 template <
class Prev>
1584 if (!input.have_value())
return input;
1585 if (ctx.occurrences > 1) {
1587 ctx.index, detail::to_error_subject(input.value)));
1601 template <
class Prev>
1603 !std::same_as<detail::decay_t<Prev>,
void>;
1605 template <
class Prev>
1608 template <
class Prev>
1614 if (!input.have_value())
return fail<void>(input.error);
1615 if (ctx.occurrences > 1) {
1617 ctx.index, detail::to_error_subject(input.value)));
1619 ctx.arg.get() = std::move(input.value);
1631 template <
class Prev>
1633 !std::same_as<detail::decay_t<Prev>,
void>;
1635 template <
class Prev>
1638 template <
class Prev>
1644 if (!input.have_value())
return fail<void>(input.error);
1645 auto& values = ctx.arg.get();
1646 if (std::ranges::find(values, input.value) != values.end()) {
1648 ctx.index, detail::to_error_subject(input.value)));
1650 values.push_back(std::move(input.value));
1662 template <
class Prev>
1664 !std::same_as<detail::decay_t<Prev>,
void>;
1666 template <
class Prev>
1669 template <
class Prev>
1675 if (!input.have_value())
return fail<void>(input.error);
1676 ctx.arg.get().push_back(std::move(input.value));
1688 template <
class Prev>
1690 !std::same_as<detail::decay_t<Prev>,
void> &&
1691 std::totally_ordered<detail::decay_t<Prev>>;
1693 template <
class Prev>
1696 template <
class Prev>
1702 if (!input.have_value())
return fail<void>(input.error);
1703 ctx.arg.get().insert(std::move(input.value));
1709 template <
class Prev>
1712 template <
class Prev>
1715 template <
class Prev,
bool = detail::PairLike<Prev>>
1719 template <
class Prev>
1722 std::remove_cvref_t<std::tuple_element_t<0, detail::decay_t<Prev>>>,
1723 std::remove_cvref_t<std::tuple_element_t<1, detail::decay_t<Prev>>>>;
1726 template <
class Prev>
1732 if (!input.have_value())
return fail<void>(input.error);
1733 auto&& [key,
value] = input.value;
1734 ctx.arg.get().insert_or_assign(key,
value);
1740 template <
class Prev>
1742 std::ranges::input_range<detail::decay_t<Prev>> &&
1743 !detail::StringLike<Prev>;
1745 template <
class Prev>
1748 template <
class Prev>
1750 std::vector<std::ranges::range_value_t<detail::decay_t<Prev>>>;
1755 if (!input.have_value())
return fail<void>(input.error);
1756 auto& out = ctx.arg.get();
1757 for (
auto&&
value : input.value) {
1758 out.emplace_back(
value);
1772 template <
class Prev>
1775 template <
class Prev>
1778 template <
class Prev>
1784 if (!input.have_value())
return fail<void>(input.error);
1785 ctx.arg.get() =
true;
1801 template <
class Prev>
1804 template <
class Prev>
1807 template <
class Prev>
1812 if (!input.have_value())
return fail<void>(input.error);
1813 if (T* ptr = ctx.arg.get(); ptr !=
nullptr) {
1814 *ptr = std::move(input.value);
1818 ctx.index,
"write_to",
1819 "target pointer is null; did you forget to call bind()?"));
1823template <auto Fn,
class T>
1825 { Fn(ctx,
value) } -> std::same_as<void>;
1826} ||
requires(T&
value) {
1827 { Fn(
value) } -> std::same_as<void>;
1829 { Fn() } -> std::same_as<void>;
1848 template <
class Prev>
1851 template <
class Prev>
1854 template <
class Prev>
1861 if (!input.have_value())
return fail<void>(input.error);
1862 if constexpr (
requires(T&
value) { Fn(ctx,
value); }) {
1863 Fn(ctx, input.value);
1864 }
else if constexpr (
requires(T&
value) { Fn(
value); }) {
1919 template <
class Prev>
1923 template <
class Prev>
1928 template <
class Prev>
1935 return Fn(ctx, std::move(input));
1947 template <
class Prev>
1950 template <
class Prev>
1953 template <
class Prev>
1956 template <
class Arg,
class T>
1972 template <
class Prev>
1975 template <
class Prev>
1978 template <
class Prev>
1981 template <
class Arg,
class T>
1984 if (!input.have_value())
return input;
1985 using U = std::remove_cvref_t<T>;
1986 if constexpr (detail::IsHelpRequested<U>::value) {
1990 .position =
static_cast<int>(ctx.index),
1996 .position =
static_cast<int>(ctx.index),
2014struct std::tuple_size<
cli::conversion::NegatableResult>
2015 : std::integral_constant<std::size_t, 2> {};
2017struct std::tuple_element<0,
cli::conversion::NegatableResult> {
2021struct std::tuple_element<1,
cli::conversion::NegatableResult> {
2027template <std::
size_t I>
2029 if constexpr (I == 0)
2035template <std::
size_t I>
2037 if constexpr (I == 0)
Actions that control the parser's control flow (help, exit).
constexpr auto print_help
constexpr auto exit_success
Actions that convert a raw std::string_view token to a typed value.
constexpr auto enumeration
constexpr auto default_missing_value
auto get(NegatableResult &r) -> decltype(auto)
Choice< T, Mapper > Enumeration
constexpr auto existing_directory
constexpr auto existing_file
Terminal actions that write the converted (and validated) value into argument storage.
constexpr auto reject_duplicate
constexpr auto push_unique
constexpr auto mark_present
constexpr auto insert_or_assign
Actions that validate a typed value after conversion.
constexpr auto non_negative
constexpr auto parent_exists
constexpr auto is_regular_file
constexpr auto is_directory
constexpr auto ignore() -> ActionResult< T >
Helper function for constructing an ignore ActionResult with type deduction.
constexpr auto ok() -> ActionResult< void >
Helper function for constructing a successful ActionResult<void>.
ErrorKind
Broad classification of an error for structured error handling.
typename std::invoke_result_t< const Step &, ActionCtx< void >, ActionResult< Input > >::value_type deduce_after_type
Deduces after_type for a non-terminal step from its operator().
@ invalid_value
A value could not be interpreted as the required type.
@ out_of_range
A numeric value fell outside the representable range of the target type.
@ duplicate_argument
An option or positional that forbids repetition was seen more than once.
@ validation_failed
A user-defined or built-in validation predicate rejected the value.
@ invalid_choice
A value was not among the allowed choices.
@ exit_success
The action pipeline requested a clean exit with code 0.
@ help_requested
The --help flag (or equivalent) was seen; print help and exit 0.
constexpr bool deduce_accepts_input
Deduces accepts_input for a non-terminal step from its operator().
constexpr auto operator|(Action< Fns... >, Action< Fn >)
constexpr auto fail(ParseError error) -> ActionResult< T >
Helper function for constructing a failure ActionResult with type deduction.
size_t total_values
Total values across all occurrences of this option.
ActionCtx(const ActionCtx< T > &other)
Constructs from a typed ActionCtx<T>, copying the counters.
size_t nargs
Number of values in this option occurrence.
size_t value_index
0-based index of current value in this occurrence.
size_t occurrences
Number of times the parent option token has appeared.
size_t index
Zero-based index into argv of the current token.
Context passed to each action function during pipeline execution.
size_t value_index
0-based index of current value in this occurrence.
size_t index
Zero-based index into argv of the current token.
size_t occurrences
Number of times the parent option token has appeared.
size_t nargs
Number of values in this option occurrence.
size_t total_values
Total values across all occurrences of this option.
std::reference_wrapper< T > arg
Reference to the argument's storage value.
constexpr auto have_value() const noexcept -> bool
Returns true when the result represents success.
static constexpr auto fail(ParseError e) -> ActionResult< void >
Creates a failure result carrying error.
constexpr auto has_error() const noexcept -> bool
Returns true when the result represents failure.
constexpr auto is_ignored() const noexcept -> bool
Returns true when the result represents an ignored step.
constexpr auto has_value() const noexcept -> bool
Returns true when the result represents success.
static constexpr auto ok() -> ActionResult< void >
Creates a success result.
static constexpr auto ignore() -> ActionResult< void >
Creates an ignore result.
The result type produced and consumed at each step of an action pipeline.
static constexpr auto ok(U &&v) -> ActionResult
Creates a success result holding value.
constexpr auto is_ignored() const noexcept -> bool
Returns true when the result represents an ignored step.
constexpr auto has_error() const noexcept -> bool
Returns true when the result represents failure.
constexpr auto has_value() const noexcept -> bool
Returns true when the result represents success.
static constexpr auto ignore() -> ActionResult
Creates an ignore result.
static constexpr auto fail(ParseError e) -> ActionResult
Creates a failure result carrying error.
constexpr auto have_value() const noexcept -> bool
Returns true when the result represents success.
static constexpr bool entry_is_optional
static constexpr auto invoke(ActionCtx< Arg > &, ActionResult< Result > input)
static constexpr auto validate() -> auto
A compile-time pipeline of action functions applied sequentially to a parsed token.
static constexpr auto invoke(ActionCtx< Arg > &ctx, ActionResult< Result > input)
static constexpr auto invoke(const ActionCtx< Arg > &ctx, ActionResult< Result > input)
static constexpr auto validate() -> auto
static constexpr bool entry_is_optional
static constexpr auto validate_impl()
Carries all context about a single parse/validation failure.
Wraps a user-defined callable as a non-terminal action pipeline step.
typename std::invoke_result_t< decltype(Fn), ActionCtx< void > &, ActionResult< Prev > >::value_type after_type
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< T > input) const -> ActionResult< after_type< T > >
Terminates the pipeline by returning a ParseError with code exit_success (or help_requested when prec...
auto operator()(ActionCtx< Arg > ctx, ActionResult< T > input) const -> ActionResult< T >
static constexpr bool accepts_input
std::monostate storage_type
Wraps the current value in a HelpRequested<T> sentinel type.
static constexpr bool accepts_input
auto operator()(ActionCtx< Arg >, ActionResult< T > input) const -> ActionResult< after_type< T > >
detail::HelpRequested< Prev > after_type
Converts a string token to bool.
static constexpr bool accepts_input
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< bool >
Converts a string token to type T via a user-supplied mapper function.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Injects a compile-time default value when an option is invoked as a flag (no value provided).
static constexpr bool accepts_input
std::string_view after_type
auto operator()(ActionCtx< void > ctx, ActionResult< std::optional< std::string_view > > input) const -> ActionResult< std::string_view >
Converts a string token to std::filesystem::path, requiring the path to exist and be a directory.
std::filesystem::path after_type
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
static constexpr bool accepts_input
Converts a string token to std::filesystem::path, requiring the path to exist and be a regular file.
std::filesystem::path after_type
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
Converts a string token to a floating-point type T using std::from_chars.
static constexpr bool accepts_input
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Converts a string token to an integral type T using std::from_chars.
static constexpr bool accepts_input
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Result of the negatable<> conversion.
Converts a string token to NegatableResult by detecting a compile-time negation prefix.
NegatableResult after_type
constexpr auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< NegatableResult >
static constexpr bool accepts_input
Converts a string token to std::filesystem::path without filesystem validation.
static constexpr bool accepts_input
auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
std::filesystem::path after_type
Converts a string_view token to a std::string by copying.
static constexpr bool accepts_input
constexpr auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< std::string >
Invokes a compile-time callable Fn with the parsed value as a terminal action.
static constexpr bool accepts_input
auto operator()(ActionCtx< std::monostate > ctx, ActionResult< T > input) const -> ActionResult< void >
std::monostate storage_type
static constexpr bool accepts_input
auto operator()(ActionCtx< storage_type< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
std::vector< std::ranges::range_value_t< detail::decay_t< Prev > > > storage_type
Increments a std::size_t counter each time the option is seen.
auto operator()(ActionCtx< std::size_t > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
std::map< std::remove_cvref_t< std::tuple_element_t< 0, detail::decay_t< Prev > > >, std::remove_cvref_t< std::tuple_element_t< 1, detail::decay_t< Prev > > > > type
typename StorageTypeImpl< Prev >::type storage_type
auto operator()(ActionCtx< storage_type< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
Inserts the value into a std::set<T> (duplicates are silently ignored by the set).
auto operator()(ActionCtx< std::set< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
std::set< detail::decay_t< Prev > > storage_type
static constexpr bool accepts_input
Sets a bool flag to true when the option is seen, regardless of value.
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
Appends the value to a std::vector<T>, rejecting duplicate values.
std::vector< detail::decay_t< Prev > > storage_type
auto operator()(ActionCtx< std::vector< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
Appends the value to a std::vector<T>.
auto operator()(ActionCtx< std::vector< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
std::vector< detail::decay_t< Prev > > storage_type
static constexpr bool accepts_input
Passes the value through unchanged, but returns ErrorCode::duplicate_argument if the option has been ...
auto operator()(ActionCtx< void > ctx, ActionResult< T > input) const -> ActionResult< T >
static constexpr bool accepts_input
Sets the bool storage to false.
static constexpr bool accepts_input
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Stores the input value and rejects subsequent occurrences.
auto operator()(ActionCtx< detail::decay_t< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
detail::decay_t< Prev > storage_type
Sets the bool storage to true.
static constexpr bool accepts_input
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Writes the value directly into an external variable via a T* pointer.
auto operator()(ActionCtx< T * > ctx, ActionResult< T > input) const -> ActionResult< void >
static constexpr bool accepts_input
Flips the bool storage each time the option is seen.
static constexpr bool accepts_input
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Validates that a std::filesystem::path exists on the filesystem.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that a std::filesystem::path refers to a directory.
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input
Validates that a std::filesystem::path refers to a regular file.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input string matches a compile-time regular expression.
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input
Validates that the input value is less than or equal to MaxValue.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input value is greater than or equal to MinValue.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input value is not empty (requires .empty() member).
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input value is non-negative (>= 0).
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input string is not blank (not all whitespace).
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input
Validates that the input value equals one of the compile-time constants Allowed.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the parent directory of a std::filesystem::path exists.
static constexpr bool accepts_input
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Validates that the input value is strictly positive (> 0).
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input
Validates a value using a user-supplied compile-time predicate.
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input
Validates that the input value is within the closed interval [MinValue, MaxValue].
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
static constexpr bool accepts_input