cli20 0.1.2
A C++20 typed-schema command-line parser
Loading...
Searching...
No Matches
action.hh
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cctype>
6#include <charconv>
7#include <concepts>
8#include <cstddef>
9#include <cstdint>
10#include <filesystem>
11#include <functional>
12#include <map>
13#include <optional>
14#include <ranges>
15#include <regex>
16#include <set>
17#include <sstream>
18#include <string>
19#include <string_view>
20#include <system_error>
21#include <type_traits>
22#include <utility>
23#include <variant>
24#include <vector>
25
26#include "cli/error.hh"
27#include "cli/string_literal.hh"
28
29namespace cli {
30
47template <class T>
49 using value_type = T;
50
51 enum class State : std::uint8_t { ok, error, ignore };
52
55 T value{};
56
58 [[nodiscard]] constexpr explicit operator bool() const noexcept {
59 return state == State::ok;
60 }
61
63 [[nodiscard]] constexpr auto have_value() const noexcept -> bool {
64 return state == State::ok;
65 }
66
68 [[nodiscard]] constexpr auto has_value() const noexcept -> bool {
69 return have_value();
70 }
71
73 [[nodiscard]] constexpr auto has_error() const noexcept -> bool {
74 return state == State::error;
75 }
76
78 [[nodiscard]] constexpr auto is_ignored() const noexcept -> bool {
79 return state == State::ignore;
80 }
81
89 template <class U>
90 [[nodiscard]] static constexpr auto ok(U&& v) -> ActionResult {
91 return {.state = State::ok, .value = std::forward<U>(v)};
92 }
93
100 [[nodiscard]] static constexpr auto fail(ParseError e) -> ActionResult {
101 return {.state = State::error, .error = std::move(e)};
102 }
103
109 [[nodiscard]] static constexpr auto ignore() -> ActionResult {
110 return {.state = State::ignore};
111 }
112};
113
123template <>
124struct ActionResult<void> {
125 using value_type = void;
126
127 enum class State : std::uint8_t { ok, error, ignore };
128
131
133 [[nodiscard]] constexpr explicit operator bool() const noexcept {
134 return state == State::ok;
135 }
136
138 [[nodiscard]] constexpr auto have_value() const noexcept -> bool {
139 return state == State::ok;
140 }
141
143 [[nodiscard]] constexpr auto has_value() const noexcept -> bool {
144 return have_value();
145 }
146
148 [[nodiscard]] constexpr auto has_error() const noexcept -> bool {
149 return state == State::error;
150 }
151
153 [[nodiscard]] constexpr auto is_ignored() const noexcept -> bool {
154 return state == State::ignore;
155 }
156
162 [[nodiscard]] static constexpr auto ok() -> ActionResult<void> {
163 return {.state = State::ok};
164 }
165
172 [[nodiscard]] static constexpr auto fail(ParseError e) -> ActionResult<void> {
173 return {.state = State::error, .error = std::move(e)};
174 }
175
181 [[nodiscard]] static constexpr auto ignore() -> ActionResult<void> {
182 return {.state = State::ignore};
183 }
184};
185
194template <class T>
195[[nodiscard]] constexpr auto ok(T&& value)
197 return ActionResult<std::remove_cvref_t<T>>::ok(std::forward<T>(value));
198}
199
205[[nodiscard]] constexpr auto ok() -> ActionResult<void> {
206 return ActionResult<void>::ok();
207}
208
217template <class T>
218[[nodiscard]] constexpr auto fail(ParseError error) -> ActionResult<T> {
219 return ActionResult<T>::fail(std::move(error));
220}
221
229template <class T>
230[[nodiscard]] constexpr auto ignore() -> ActionResult<T> {
232}
233
245template <class T = void>
246struct ActionCtx {
247 size_t index{};
248 size_t
250 size_t nargs{};
251 size_t value_index{};
252 size_t
254 std::reference_wrapper<T>
255 arg{};
256};
257
267template <>
268struct ActionCtx<void> {
269 size_t index{};
270 size_t
272 size_t nargs{};
273 size_t value_index{};
274 size_t
276
283 template <class T>
285 : index(other.index),
287 nargs(other.nargs),
289 total_values(other.total_values) {}
290
291 ActionCtx() = default;
292};
293
294namespace detail {
295
296template <class T>
297[[nodiscard]]
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);
304 } else {
305 return std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
306 }
307 }();
308 if (ec != std::errc{}) return "<value>";
309 return std::string(buffer.data(), ptr);
310}
311
312template <class T>
313[[nodiscard]]
314auto to_error_subject(const T& value) -> std::string {
315 using U = std::remove_cvref_t<T>;
316
317 if constexpr (std::same_as<U, std::string>) {
318 return value;
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;
331 out << value;
332 return out.str();
333 } else {
334 return "<value>";
335 }
336}
337
338[[nodiscard]]
339inline auto invalid_value_error(ErrorKind kind, std::size_t index,
340 std::string subject, std::string detail = {})
341 -> ParseError {
342 return ParseError{
344 .kind = kind,
345 .position = static_cast<int>(index),
346 .subject = std::move(subject),
347 .detail = std::move(detail),
348 };
349}
350
351[[nodiscard]]
352inline auto duplicate_argument_error(std::size_t index, std::string subject,
353 std::string detail = {}) -> ParseError {
354 return ParseError{
356 .kind = ErrorKind::validation,
357 .position = static_cast<int>(index),
358 .subject = std::move(subject),
359 .detail = std::move(detail),
360 };
361}
362
363[[nodiscard]]
364inline auto validation_failed_error(std::size_t index, std::string subject,
365 std::string detail) -> ParseError {
366 return ParseError{
368 .kind = ErrorKind::validation,
369 .position = static_cast<int>(index),
370 .subject = std::move(subject),
371 .detail = std::move(detail),
372 };
373}
374
375[[nodiscard]]
376inline auto invalid_choice_error(std::size_t index, std::string subject,
377 std::string detail = {}) -> ParseError {
378 return ParseError{
380 .kind = ErrorKind::conversion,
381 .position = static_cast<int>(index),
382 .subject = std::move(subject),
383 .detail = std::move(detail),
384 };
385}
386
387[[nodiscard]]
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; });
391}
392
393template <class T>
394using decay_t = std::remove_cvref_t<T>;
395
396template <class T>
397struct HelpRequested {
398 T value;
399};
400
401template <class T>
402struct IsHelpRequested : std::false_type {};
403
404template <class T>
405struct IsHelpRequested<HelpRequested<T>> : std::true_type {};
406
407template <class T>
408concept PairLike = requires {
409 typename std::tuple_size<decay_t<T>>::type;
410 requires std::tuple_size_v<decay_t<T>> == 2;
411};
412
413template <class T>
414concept StringLike = std::same_as<decay_t<T>, std::string> ||
415 std::same_as<decay_t<T>, std::string_view>;
416
417} // namespace detail
418
443template <auto... Fns>
444 requires(requires {
445 {
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>;
449 } && ...)
450struct Action {
451 template <class Arg, class Result>
452 [[nodiscard]]
453 static constexpr auto invoke(ActionCtx<Arg>& ctx, ActionResult<Result> input) {
454 return
455 []<auto FnHead, auto... FnTail>(
456 ActionCtx<Arg>& ctx, ActionResult<Result> input) -> decltype(auto) {
457 if (input.is_ignored()) {
458 using FinalIdentity = std::remove_cvref_t<decltype(std::get<2>(
459 Action::template validate_impl<false, Result, FnHead,
460 FnTail...>()))>;
462 }
463
464 auto next = FnHead(ctx, std::move(input));
465 if constexpr (sizeof...(FnTail) == 0) {
466 return next;
467 } else {
468 return Action<FnTail...>::invoke(ctx, std::move(next));
469 }
470 }.template operator()<Fns...>(ctx, std::move(input));
471 }
472
473 template <class Arg, class Result>
474 [[nodiscard]]
475 static constexpr auto invoke(const ActionCtx<Arg>& ctx,
476 ActionResult<Result> input) {
477 return []<auto FnHead, auto... FnTail>(
478 const ActionCtx<Arg>& ctx,
479 ActionResult<Result> input) -> decltype(auto) {
480 if (input.is_ignored()) {
481 using FinalIdentity = std::remove_cvref_t<decltype(std::get<2>(
482 Action::template validate_impl<false, Result, FnHead,
483 FnTail...>()))>;
485 }
486
487 auto next = FnHead(ctx, std::move(input));
488 if constexpr (sizeof...(FnTail) == 0) {
489 return next;
490 } else {
491 return Action<FnTail...>::invoke(ctx, std::move(next));
492 }
493 }.template operator()<Fns...>(ctx, std::move(input));
494 }
495
496 template <bool EnableTailCheck, class Prev, auto FnHead, auto... FnTail>
497 static constexpr auto validate_impl() {
498 static_assert(
499 !EnableTailCheck ||
500 (sizeof...(FnTail) != 0 ||
501 !std::same_as<typename std::remove_cvref_t<
502 decltype(FnHead)>::template storage_type<Prev>,
503 void>),
504 "The last action must have a non-void storage type");
505
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>>{});
512 } else {
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>{});
518 } else {
519 using Next = typename Head::template after_type<Prev>;
520 return validate_impl<EnableTailCheck, Next, FnTail...>();
521 }
522 }
523 }
524
525 private:
526 template <auto First, auto...>
527 struct FirstFnType {
528 using type = std::remove_cvref_t<decltype(First)>;
529 };
530
531 public:
532 static constexpr bool entry_is_optional = FirstFnType<
533 Fns...>::type::template accepts_input<std::optional<std::string_view>>;
534
535 static constexpr auto validate() -> auto {
536 using FirstFn = typename FirstFnType<Fns...>::type;
537 if constexpr (FirstFn::template accepts_input<
538 std::optional<std::string_view>>) {
540 } else {
541 return validate_impl<true, std::string_view, Fns...>();
542 }
543 }
544};
545
546template <>
547struct Action<> {
548 static constexpr bool entry_is_optional = false;
549
550 template <class Arg, class Result>
551 [[nodiscard]]
552 static constexpr auto invoke(ActionCtx<Arg>&, ActionResult<Result> input) {
553 return input;
554 }
555
556 static constexpr auto validate() -> auto {
557 return std::make_tuple(true, std::type_identity<std::monostate>{},
558 std::type_identity<std::monostate>{});
559 }
560};
561
562template <auto Fn, auto... Fns>
564 return Action<Fns..., Fn>{};
565}
566
587template <class Step, class Input>
588inline constexpr bool deduce_accepts_input =
589 std::invocable<const Step&, ActionCtx<void>, ActionResult<Input>>;
590
616template <class Step, class Input>
619 typename std::invoke_result_t<const Step&, ActionCtx<void>,
620 ActionResult<Input>>::value_type;
621
635namespace conversion {
636
646template <std::integral T>
647struct Integer {
648 template <class Input>
649 static constexpr bool accepts_input =
650 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
651 std::same_as<std::remove_cvref_t<Input>, std::string>;
652
653 template <class Input>
654 using after_type = T;
655
656 template <class Prev>
657 using storage_type = void;
658
659 constexpr auto operator()(ActionCtx<void> ctx,
661 -> ActionResult<T> {
662 if (!input.have_value()) return fail<T>(input.error);
663 T result{};
664 auto r = std::from_chars(input.value.data(),
665 input.value.data() + input.value.size(), result);
666
667 if (r.ec == std::errc::invalid_argument) {
668 return ActionResult<T>::fail(detail::invalid_value_error(
669 ErrorKind::conversion, ctx.index, std::string(input.value)));
670 }
671 if (r.ec == std::errc::result_out_of_range) {
674 .kind = ErrorKind::conversion,
675 .position = static_cast<int>(ctx.index),
676 .subject = std::string(input.value),
677 });
678 }
679 if (r.ptr != input.value.data() + input.value.size()) {
680 return ActionResult<T>::fail(detail::invalid_value_error(
681 ErrorKind::conversion, ctx.index, std::string(input.value),
682 "unexpected trailing characters"));
683 }
684
685 return ok(result);
686 }
687};
688
698template <std::floating_point T>
699struct Floating {
700 template <class Input>
701 static constexpr bool accepts_input =
702 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
703 std::same_as<std::remove_cvref_t<Input>, std::string>;
704
705 template <class Input>
706 using after_type = T;
707
708 template <class Prev>
709 using storage_type = void;
710
711 constexpr auto operator()(ActionCtx<void> ctx,
713 -> ActionResult<T> {
714 if (!input.have_value()) return fail<T>(input.error);
715 T result{};
716 auto r = std::from_chars(input.value.data(),
717 input.value.data() + input.value.size(), result);
718
719 if (r.ec == std::errc::invalid_argument) {
720 return ActionResult<T>::fail(detail::invalid_value_error(
721 ErrorKind::conversion, ctx.index, std::string(input.value)));
722 }
723 if (r.ec == std::errc::result_out_of_range) {
726 .kind = ErrorKind::conversion,
727 .position = static_cast<int>(ctx.index),
728 .subject = std::string(input.value),
729 });
730 }
731 if (r.ptr != input.value.data() + input.value.size()) {
732 return ActionResult<T>::fail(detail::invalid_value_error(
733 ErrorKind::conversion, ctx.index, std::string(input.value),
734 "unexpected trailing characters"));
735 }
736
737 return ok(result);
738 }
739};
740
742struct String {
743 template <class Input>
744 static constexpr bool accepts_input =
745 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
746 std::same_as<std::remove_cvref_t<Input>, std::string>;
747
748 template <class Input>
749 using after_type = std::string;
750
751 template <class Prev>
752 using storage_type = void;
753
757 if (!input.have_value()) return fail<std::string>(input.error);
758 return ok(std::string(input.value));
759 }
760};
761
768struct Bool {
769 template <class Input>
770 static constexpr bool accepts_input =
771 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
772 std::same_as<std::remove_cvref_t<Input>, std::string>;
773
774 template <class Input>
775 using after_type = bool;
776
777 template <class Prev>
778 using storage_type = void;
779
780 constexpr auto operator()(ActionCtx<void> ctx,
783 if (!input.have_value()) return fail<bool>(input.error);
784 const std::string_view val = input.value;
785 if (val == "true" || val == "1") {
786 return ok(true);
787 }
788 if (val == "false" || val == "0") {
789 return ok(false);
790 }
791 return ActionResult<bool>::fail(detail::invalid_value_error(
792 ErrorKind::conversion, ctx.index, std::string(input.value),
793 "expected one of: true, false, 1, 0"));
794 }
795};
796
799struct Path {
800 template <class Input>
801 static constexpr bool accepts_input =
802 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
803 std::same_as<std::remove_cvref_t<Input>, std::string>;
804
805 template <class Input>
806 using after_type = std::filesystem::path;
807
808 template <class Prev>
809 using storage_type = void;
810
813 if (!input.have_value()) return fail<std::filesystem::path>(input.error);
814 return ok(std::filesystem::path{input.value});
815 }
816};
817
826 template <class Input>
827 static constexpr bool accepts_input = Path::template accepts_input<Input>;
828
829 template <class Input>
830 using after_type = std::filesystem::path;
831
832 template <class Prev>
833 using storage_type = void;
834
838 if (!input.have_value()) return fail<std::filesystem::path>(input.error);
839 auto path = std::filesystem::path{input.value};
840 if (!std::filesystem::exists(path) ||
841 !std::filesystem::is_regular_file(path)) {
843 detail::invalid_value_error(ErrorKind::conversion, ctx.index,
844 path.string(),
845 "expected an existing regular file"));
846 }
847 return ok(std::move(path));
848 }
849};
850
859 template <class Input>
860 static constexpr bool accepts_input = Path::template accepts_input<Input>;
861
862 template <class Input>
863 using after_type = std::filesystem::path;
864
865 template <class Prev>
866 using storage_type = void;
867
871 if (!input.have_value()) return fail<std::filesystem::path>(input.error);
872 auto path = std::filesystem::path{input.value};
873 if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) {
875 detail::invalid_value_error(ErrorKind::conversion, ctx.index,
876 path.string(),
877 "expected an existing directory"));
878 }
879 return ok(std::move(path));
880 }
881};
882
894template <class T, auto Mapper>
895struct Choice {
896 template <class Input>
897 static constexpr bool accepts_input = Bool::template accepts_input<Input>;
898
899 template <class Input>
900 using after_type = T;
901
902 template <class Prev>
903 using storage_type = void;
904
907 -> ActionResult<T> {
908 if (!input.have_value()) return fail<T>(input.error);
909 if (auto value = Mapper(input.value); value.has_value()) {
910 return ok(*std::move(value));
911 }
913 detail::invalid_choice_error(ctx.index, std::string(input.value)));
914 }
915};
916
917template <class T, auto Mapper>
919
920template <std::integral T>
921inline constexpr auto integer = Action<Integer<T>{}>{};
922template <std::floating_point T>
923inline constexpr auto floating = Action<Floating<T>{}>{};
924template <class T, auto Mapper>
925inline constexpr auto choice = Action<Choice<T, Mapper>{}>{};
926template <class T, auto Mapper>
927inline constexpr auto enumeration = Action<Enumeration<T, Mapper>{}>{};
935 std::string name;
937};
938
949template <StringLiteral Prefix>
950struct Negatable {
951 template <class Input>
952 static constexpr bool accepts_input =
953 std::same_as<std::remove_cvref_t<Input>, std::string_view> ||
954 std::same_as<std::remove_cvref_t<Input>, std::string>;
955
956 template <class Input>
958
959 template <class Prev>
960 using storage_type = void;
961
965 if (!input.have_value()) return fail<NegatableResult>(input.error);
966 constexpr std::string_view prefix = Prefix.view();
967 const std::string_view val = input.value;
968 if (val.starts_with(prefix)) {
969 return ok(NegatableResult{.name = std::string(val.substr(prefix.size())),
970 .enabled = false});
971 }
972 return ok(NegatableResult{.name = std::string(val), .enabled = true});
973 }
974};
975
976template <StringLiteral Prefix>
977inline constexpr auto negatable = Action<Negatable<Prefix>{}>{};
978inline constexpr auto string = Action<String{}>{};
979inline constexpr auto boolean = Action<Bool{}>{};
980inline constexpr auto path = Action<Path{}>{};
981inline constexpr auto existing_file = Action<ExistingFile{}>{};
982inline constexpr auto existing_directory = Action<ExistingDirectory{}>{};
983
997template <StringLiteral Value>
999 template <class Input>
1000 static constexpr bool accepts_input =
1001 std::same_as<std::remove_cvref_t<Input>, std::optional<std::string_view>>;
1002
1003 template <class Input>
1004 using after_type = std::string_view;
1005
1006 template <class Prev>
1007 using storage_type = void;
1008
1010 ActionResult<std::optional<std::string_view>> input) const
1012 if (!input.have_value()) return fail<std::string_view>(input.error);
1013 if (input.value.has_value()) {
1014 // actual value provided: unwrap and pass through
1015 return ok(std::string_view(*input.value));
1016 }
1017 // nullopt: flag invocation
1018 if (ctx.nargs == 0) {
1019 // pure flag with no value: inject the compile-time default
1020 return ok(Value.view());
1021 }
1022 // flag seen but a value is coming via separate value invocation: ignore
1024 }
1025};
1026
1027template <StringLiteral Value>
1030
1031} // namespace conversion
1032
1046namespace validation {
1047
1053template <auto MinValue>
1054struct Min {
1055 template <class Prev>
1056 static constexpr bool accepts_input =
1057 std::totally_ordered<std::remove_cvref_t<Prev>>;
1058
1059 template <class Prev>
1060 using after_type = Prev;
1061
1062 template <class Prev>
1063 using storage_type = void;
1064
1065 template <class U>
1067 -> ActionResult<U> {
1068 if (!input.have_value()) return input;
1069 if (input.value < MinValue) {
1070 return ActionResult<U>::fail(detail::validation_failed_error(
1071 ctx.index, detail::to_error_subject(input.value),
1072 "value must be >= " + detail::to_error_subject(MinValue)));
1073 }
1074 return input;
1075 }
1076};
1077
1083template <auto MaxValue>
1084struct Max {
1085 template <class Prev>
1086 static constexpr bool accepts_input =
1087 std::totally_ordered<std::remove_cvref_t<Prev>>;
1088
1089 template <class Prev>
1090 using after_type = Prev;
1091
1092 template <class Prev>
1093 using storage_type = void;
1094
1095 template <class U>
1097 -> ActionResult<U> {
1098 if (!input.have_value()) return input;
1099 if (input.value > MaxValue) {
1100 return ActionResult<U>::fail(detail::validation_failed_error(
1101 ctx.index, detail::to_error_subject(input.value),
1102 "value must be <= " + detail::to_error_subject(MaxValue)));
1103 }
1104 return input;
1105 }
1106};
1107
1115template <auto MinValue, auto MaxValue>
1116 requires std::is_same_v<decltype(MinValue), decltype(MaxValue)>
1117struct Range {
1118 template <class Prev>
1119 static constexpr bool accepts_input =
1120 std::totally_ordered<std::remove_cvref_t<Prev>>;
1121
1122 template <class Prev>
1123 using after_type = Prev;
1124
1125 template <class Prev>
1126 using storage_type = void;
1127
1128 template <class U>
1130 -> ActionResult<U> {
1131 if (!input.have_value()) return input;
1132 if (input.value < MinValue || input.value > MaxValue) {
1133 return ActionResult<U>::fail(detail::validation_failed_error(
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)));
1137 }
1138 return input;
1139 }
1140};
1141
1143struct Positive {
1144 template <class Prev>
1145 static constexpr bool accepts_input =
1146 std::totally_ordered<detail::decay_t<Prev>>;
1147
1148 template <class Prev>
1149 using after_type = Prev;
1150
1151 template <class Prev>
1152 using storage_type = void;
1153
1154 template <class U>
1156 -> ActionResult<U> {
1157 if (!input.have_value()) return input;
1158 if (!(input.value > 0)) {
1159 return ActionResult<U>::fail(detail::validation_failed_error(
1160 ctx.index, detail::to_error_subject(input.value),
1161 "value must be positive"));
1162 }
1163 return input;
1164 }
1165};
1166
1169 template <class Prev>
1170 static constexpr bool accepts_input =
1171 std::totally_ordered<detail::decay_t<Prev>>;
1172
1173 template <class Prev>
1174 using after_type = Prev;
1175
1176 template <class Prev>
1177 using storage_type = void;
1178
1179 template <class U>
1181 -> ActionResult<U> {
1182 if (!input.have_value()) return input;
1183 if (input.value < 0) {
1184 return ActionResult<U>::fail(detail::validation_failed_error(
1185 ctx.index, detail::to_error_subject(input.value),
1186 "value must be non-negative"));
1187 }
1188 return input;
1189 }
1190};
1191
1194struct NonEmpty {
1195 template <class Prev>
1196 static constexpr bool accepts_input =
1197 requires(const detail::decay_t<Prev>& value) { value.empty(); };
1198
1199 template <class Prev>
1200 using after_type = Prev;
1201
1202 template <class Prev>
1203 using storage_type = void;
1204
1205 template <class U>
1207 -> ActionResult<U> {
1208 if (!input.have_value()) return input;
1209 if (input.value.empty()) {
1210 return ActionResult<U>::fail(detail::validation_failed_error(
1211 ctx.index, detail::to_error_subject(input.value),
1212 "value must not be empty"));
1213 }
1214 return input;
1215 }
1216};
1217
1219struct NotBlank {
1220 template <class Prev>
1221 static constexpr bool accepts_input =
1222 detail::StringLike<Prev> ||
1223 std::same_as<detail::decay_t<Prev>, const char*>;
1224
1225 template <class Prev>
1226 using after_type = Prev;
1227
1228 template <class Prev>
1229 using storage_type = void;
1230
1231 template <class U>
1233 -> ActionResult<U> {
1234 if (!input.have_value()) return input;
1235 std::string_view text = input.value;
1236 if (detail::is_blank(text)) {
1237 return ActionResult<U>::fail(detail::validation_failed_error(
1238 ctx.index, std::string(text), "value must not be blank"));
1239 }
1240 return input;
1241 }
1242};
1243
1251template <auto... Allowed>
1252struct OneOf {
1253 template <class Prev>
1254 static constexpr bool accepts_input =
1255 ((std::same_as<detail::decay_t<Prev>,
1256 detail::decay_t<decltype(Allowed)>>) &&
1257 ...);
1258
1259 template <class Prev>
1260 using after_type = Prev;
1261
1262 template <class Prev>
1263 using storage_type = void;
1264
1265 template <class U>
1267 -> ActionResult<U> {
1268 if (!input.have_value()) return input;
1269 if (((input.value == Allowed) || ...)) {
1270 return input;
1271 }
1272 return ActionResult<U>::fail(detail::validation_failed_error(
1273 ctx.index, detail::to_error_subject(input.value),
1274 "value was not one of the allowed choices"));
1275 }
1276};
1277
1287template <StringLiteral Pattern>
1288struct Matches {
1289 template <class Prev>
1290 static constexpr bool accepts_input = detail::StringLike<Prev>;
1291
1292 template <class Prev>
1293 using after_type = Prev;
1294
1295 template <class Prev>
1296 using storage_type = void;
1297
1298 template <class U>
1300 -> ActionResult<U> {
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)) {
1304 return ActionResult<U>::fail(detail::validation_failed_error(
1305 ctx.index, detail::to_error_subject(input.value),
1306 "value must match " + std::string(Pattern.view())));
1307 }
1308 return input;
1309 }
1310};
1311
1313struct Exists {
1314 template <class Prev>
1315 static constexpr bool accepts_input =
1316 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1317
1318 template <class Prev>
1319 using after_type = Prev;
1320
1321 template <class Prev>
1322 using storage_type = void;
1323
1324 template <class U>
1326 -> ActionResult<U> {
1327 if (!input.have_value()) return input;
1328 if (!std::filesystem::exists(input.value)) {
1329 return ActionResult<U>::fail(detail::validation_failed_error(
1330 ctx.index, input.value.string(), "path does not exist"));
1331 }
1332 return input;
1333 }
1334};
1335
1338 template <class Prev>
1339 static constexpr bool accepts_input =
1340 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1341
1342 template <class Prev>
1343 using after_type = Prev;
1344
1345 template <class Prev>
1346 using storage_type = void;
1347
1348 template <class U>
1350 -> ActionResult<U> {
1351 if (!input.have_value()) return input;
1352 if (!std::filesystem::is_regular_file(input.value)) {
1353 return ActionResult<U>::fail(detail::validation_failed_error(
1354 ctx.index, input.value.string(), "path is not a regular file"));
1355 }
1356 return input;
1357 }
1358};
1359
1362 template <class Prev>
1363 static constexpr bool accepts_input =
1364 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1365
1366 template <class Prev>
1367 using after_type = Prev;
1368
1369 template <class Prev>
1370 using storage_type = void;
1371
1372 template <class U>
1374 -> ActionResult<U> {
1375 if (!input.have_value()) return input;
1376 if (!std::filesystem::is_directory(input.value)) {
1377 return ActionResult<U>::fail(detail::validation_failed_error(
1378 ctx.index, input.value.string(), "path is not a directory"));
1379 }
1380 return input;
1381 }
1382};
1383
1387 template <class Prev>
1388 static constexpr bool accepts_input =
1389 std::same_as<detail::decay_t<Prev>, std::filesystem::path>;
1390
1391 template <class Prev>
1392 using after_type = Prev;
1393
1394 template <class Prev>
1395 using storage_type = void;
1396
1397 template <class U>
1399 -> ActionResult<U> {
1400 if (!input.have_value()) return input;
1401 const auto parent = input.value.parent_path();
1402 if (!parent.empty() && !std::filesystem::exists(parent)) {
1403 return ActionResult<U>::fail(detail::validation_failed_error(
1404 ctx.index, input.value.string(), "parent directory does not exist"));
1405 }
1406 return input;
1407 }
1408};
1409
1418template <auto Pred>
1420 template <class Prev>
1421 static constexpr bool accepts_input =
1422 requires(const detail::decay_t<Prev>& value) {
1423 { Pred(value) } -> std::convertible_to<bool>;
1424 };
1425
1426 template <class Prev>
1427 using after_type = Prev;
1428
1429 template <class Prev>
1430 using storage_type = void;
1431
1432 template <class U>
1434 -> ActionResult<U> {
1435 if (!input.have_value()) return input;
1436 if (!Pred(input.value)) {
1437 return ActionResult<U>::fail(detail::validation_failed_error(
1438 ctx.index, detail::to_error_subject(input.value),
1439 "predicate rejected value"));
1440 }
1441 return input;
1442 }
1443};
1444
1445template <auto MinValue>
1446inline constexpr auto min = Action<Min<MinValue>{}>{};
1447template <auto MaxValue>
1448inline constexpr auto max = Action<Max<MaxValue>{}>{};
1449template <auto MinValue, auto MaxValue>
1450inline constexpr auto range = Action<Range<MinValue, MaxValue>{}>{};
1451template <auto... Allowed>
1452inline constexpr auto one_of = Action<OneOf<Allowed...>{}>{};
1453template <StringLiteral Pattern>
1454inline constexpr auto matches = Action<Matches<Pattern>{}>{};
1455template <auto Pred>
1456inline constexpr auto predicate = Action<Predicate<Pred>{}>{};
1457inline constexpr auto positive = Action<Positive{}>{};
1458inline constexpr auto non_negative = Action<NonNegative{}>{};
1459inline constexpr auto non_empty = Action<NonEmpty{}>{};
1460inline constexpr auto not_blank = Action<NotBlank{}>{};
1461inline constexpr auto exists = Action<Exists{}>{};
1462inline constexpr auto is_regular_file = Action<IsRegularFile{}>{};
1463inline constexpr auto is_directory = Action<IsDirectory{}>{};
1464inline constexpr auto parent_exists = Action<ParentExists{}>{};
1465
1466} // namespace validation
1467
1481namespace pack {
1482
1484struct SetTrue {
1485 template <class Prev>
1486 static constexpr bool accepts_input = true;
1487
1488 template <class Prev>
1489 using after_type = void;
1490
1491 template <class Prev>
1492 using storage_type = bool;
1493
1494 template <class T>
1497 if (!input.have_value()) return fail<void>(input.error);
1498 ctx.arg.get() = true;
1499 return ok();
1500 }
1501};
1502
1504struct SetFalse {
1505 template <class Prev>
1506 static constexpr bool accepts_input = true;
1507
1508 template <class Prev>
1509 using after_type = void;
1510
1511 template <class Prev>
1512 using storage_type = bool;
1513
1514 template <class T>
1517 if (!input.have_value()) return fail<void>(input.error);
1518 ctx.arg.get() = false;
1519 return ok();
1520 }
1521};
1522
1524struct Toggle {
1525 template <class Prev>
1526 static constexpr bool accepts_input = true;
1527
1528 template <class Prev>
1529 using after_type = void;
1530
1531 template <class Prev>
1532 using storage_type = bool;
1533
1534 template <class T>
1537 if (!input.have_value()) return fail<void>(input.error);
1538 ctx.arg.get() = !ctx.arg.get();
1539 return ok();
1540 }
1541};
1542
1545 template <class Prev>
1546 static constexpr bool accepts_input = true;
1547
1548 template <class Prev>
1549 using after_type = void;
1550
1551 template <class Prev>
1552 using storage_type = std::size_t;
1553
1554 template <class T>
1557 if (!input.have_value()) return fail<void>(input.error);
1558 ++ctx.arg.get();
1559 return ok();
1560 }
1561};
1562
1571 template <class Prev>
1572 static constexpr bool accepts_input =
1573 !std::same_as<detail::decay_t<Prev>, void>;
1574
1575 template <class Prev>
1576 using after_type = Prev;
1577
1578 template <class Prev>
1579 using storage_type = void;
1580
1581 template <class T>
1583 -> ActionResult<T> {
1584 if (!input.have_value()) return input;
1585 if (ctx.occurrences > 1) {
1586 return ActionResult<T>::fail(detail::duplicate_argument_error(
1587 ctx.index, detail::to_error_subject(input.value)));
1588 }
1589 return input;
1590 }
1591};
1592
1600struct SetOnce {
1601 template <class Prev>
1602 static constexpr bool accepts_input =
1603 !std::same_as<detail::decay_t<Prev>, void>;
1604
1605 template <class Prev>
1606 using after_type = void;
1607
1608 template <class Prev>
1609 using storage_type = detail::decay_t<Prev>;
1610
1611 template <class T>
1612 auto operator()(ActionCtx<detail::decay_t<T>> ctx, ActionResult<T> input) const
1614 if (!input.have_value()) return fail<void>(input.error);
1615 if (ctx.occurrences > 1) {
1616 return ActionResult<void>::fail(detail::duplicate_argument_error(
1617 ctx.index, detail::to_error_subject(input.value)));
1618 }
1619 ctx.arg.get() = std::move(input.value);
1620 return ok();
1621 }
1622};
1623
1631 template <class Prev>
1632 static constexpr bool accepts_input =
1633 !std::same_as<detail::decay_t<Prev>, void>;
1634
1635 template <class Prev>
1636 using after_type = void;
1637
1638 template <class Prev>
1639 using storage_type = std::vector<detail::decay_t<Prev>>;
1640
1641 template <class T>
1642 auto operator()(ActionCtx<std::vector<detail::decay_t<T>>> ctx,
1643 ActionResult<T> input) const -> ActionResult<void> {
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()) {
1647 return ActionResult<void>::fail(detail::duplicate_argument_error(
1648 ctx.index, detail::to_error_subject(input.value)));
1649 }
1650 values.push_back(std::move(input.value));
1651 return ok();
1652 }
1653};
1654
1661struct Push {
1662 template <class Prev>
1663 static constexpr bool accepts_input =
1664 !std::same_as<detail::decay_t<Prev>, void>;
1665
1666 template <class Prev>
1667 using after_type = void;
1668
1669 template <class Prev>
1670 using storage_type = std::vector<detail::decay_t<Prev>>;
1671
1672 template <class T>
1673 auto operator()(ActionCtx<std::vector<detail::decay_t<T>>> ctx,
1674 ActionResult<T> input) const -> ActionResult<void> {
1675 if (!input.have_value()) return fail<void>(input.error);
1676 ctx.arg.get().push_back(std::move(input.value));
1677 return ok();
1678 }
1679};
1680
1687struct Insert {
1688 template <class Prev>
1689 static constexpr bool accepts_input =
1690 !std::same_as<detail::decay_t<Prev>, void> &&
1691 std::totally_ordered<detail::decay_t<Prev>>;
1692
1693 template <class Prev>
1694 using after_type = void;
1695
1696 template <class Prev>
1697 using storage_type = std::set<detail::decay_t<Prev>>;
1698
1699 template <class T>
1700 auto operator()(ActionCtx<std::set<detail::decay_t<T>>> ctx,
1701 ActionResult<T> input) const -> ActionResult<void> {
1702 if (!input.have_value()) return fail<void>(input.error);
1703 ctx.arg.get().insert(std::move(input.value));
1704 return ok();
1705 }
1706};
1707
1709 template <class Prev>
1710 static constexpr bool accepts_input = detail::PairLike<Prev>;
1711
1712 template <class Prev>
1713 using after_type = void;
1714
1715 template <class Prev, bool = detail::PairLike<Prev>>
1717 using type = void;
1718 };
1719 template <class Prev>
1720 struct StorageTypeImpl<Prev, true> {
1721 using type = std::map<
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>>>>;
1724 };
1725
1726 template <class Prev>
1728
1729 template <class T>
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);
1735 return ok();
1736 }
1737};
1738
1739struct Extend {
1740 template <class Prev>
1741 static constexpr bool accepts_input =
1742 std::ranges::input_range<detail::decay_t<Prev>> &&
1743 !detail::StringLike<Prev>;
1744
1745 template <class Prev>
1746 using after_type = void;
1747
1748 template <class Prev>
1750 std::vector<std::ranges::range_value_t<detail::decay_t<Prev>>>;
1751
1752 template <class T>
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);
1759 }
1760 return ok();
1761 }
1762};
1763
1772 template <class Prev>
1773 static constexpr bool accepts_input = true;
1774
1775 template <class Prev>
1776 using after_type = void;
1777
1778 template <class Prev>
1779 using storage_type = bool;
1780
1781 template <class T>
1784 if (!input.have_value()) return fail<void>(input.error);
1785 ctx.arg.get() = true;
1786 return ok();
1787 }
1788};
1789
1799template <class T>
1801 template <class Prev>
1802 static constexpr bool accepts_input = std::same_as<detail::decay_t<Prev>, T>;
1803
1804 template <class Prev>
1805 using after_type = void;
1806
1807 template <class Prev>
1808 using storage_type = T*;
1809
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);
1815 return ok();
1816 }
1817 return ActionResult<void>::fail(detail::validation_failed_error(
1818 ctx.index, "write_to",
1819 "target pointer is null; did you forget to call bind()?"));
1820 }
1821};
1822
1823template <auto Fn, class T>
1825 { Fn(ctx, value) } -> std::same_as<void>;
1826} || requires(T& value) {
1827 { Fn(value) } -> std::same_as<void>;
1828} || requires {
1829 { Fn() } -> std::same_as<void>;
1830};
1831
1846template <auto Fn>
1847struct Callback {
1848 template <class Prev>
1850
1851 template <class Prev>
1852 using after_type = void;
1853
1854 template <class Prev>
1855 using storage_type = std::monostate;
1856
1857 template <class T>
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); }) {
1865 Fn(input.value);
1866 } else {
1867 Fn();
1868 }
1869 return ok();
1870 }
1871};
1872
1873inline constexpr auto set_true = Action<SetTrue{}>{};
1874inline constexpr auto set_false = Action<SetFalse{}>{};
1875inline constexpr auto toggle = Action<Toggle{}>{};
1876inline constexpr auto increment = Action<Increment{}>{};
1877inline constexpr auto set_once = Action<SetOnce{}>{};
1878inline constexpr auto reject_duplicate = Action<RejectDuplicate{}>{};
1879inline constexpr auto push_unique = Action<PushUnique{}>{};
1880inline constexpr auto push = Action<Push{}>{};
1881inline constexpr auto insert = Action<Insert{}>{};
1882inline constexpr auto insert_or_assign = Action<InsertOrAssign{}>{};
1883// inline constexpr auto extend = Action<Extend{}>{};
1884inline constexpr auto mark_present = Action<MarkPresent{}>{};
1885template <auto Fn>
1886inline constexpr auto callback = Action<Callback<Fn>{}>{};
1887template <class T>
1888inline constexpr auto write_to = Action<StoreInto<T>{}>{};
1889
1890} // namespace pack
1891
1895namespace action {
1896
1917template <auto Fn>
1918struct Custom {
1919 template <class Prev>
1920 static constexpr bool accepts_input =
1921 std::invocable<decltype(Fn), ActionCtx<void>&, ActionResult<Prev>>;
1922
1923 template <class Prev>
1925 typename std::invoke_result_t<decltype(Fn), ActionCtx<void>&,
1926 ActionResult<Prev>>::value_type;
1927
1928 template <class Prev>
1929 using storage_type = void;
1930
1931 template <class T>
1932 requires accepts_input<T>
1935 return Fn(ctx, std::move(input));
1936 }
1937};
1938
1947 template <class Prev>
1948 static constexpr bool accepts_input = true;
1949
1950 template <class Prev>
1951 using after_type = detail::HelpRequested<Prev>;
1952
1953 template <class Prev>
1954 using storage_type = void;
1955
1956 template <class Arg, class T>
1959 if (!input.have_value()) return fail<after_type<T>>(input.error);
1960 return ok(after_type<T>{.value = std::move(input.value)});
1961 }
1962};
1963
1972 template <class Prev>
1973 static constexpr bool accepts_input = true;
1974
1975 template <class Prev>
1976 using after_type = Prev;
1977
1978 template <class Prev>
1979 using storage_type = std::monostate;
1980
1981 template <class Arg, class T>
1983 -> ActionResult<T> {
1984 if (!input.have_value()) return input;
1985 using U = std::remove_cvref_t<T>;
1986 if constexpr (detail::IsHelpRequested<U>::value) {
1989 .kind = ErrorKind::parse,
1990 .position = static_cast<int>(ctx.index),
1991 });
1992 } else {
1995 .kind = ErrorKind::parse,
1996 .position = static_cast<int>(ctx.index),
1997 });
1998 }
1999 }
2000};
2001
2002inline constexpr auto print_help = Action<PrintHelp{}>{};
2003inline constexpr auto exit_success = Action<ExitSuccess{}>{};
2004template <auto Fn>
2005inline constexpr auto custom = Action<Custom<Fn>{}>{};
2006
2007} // namespace action
2008
2009} // namespace cli
2010
2011// Tuple protocol for NegatableResult — required for PairLike and structured
2012// bindings with pack::insert_or_assign.
2013template <>
2014struct std::tuple_size<cli::conversion::NegatableResult>
2015 : std::integral_constant<std::size_t, 2> {};
2016template <>
2017struct std::tuple_element<0, cli::conversion::NegatableResult> {
2018 using type = std::string;
2019};
2020template <>
2021struct std::tuple_element<1, cli::conversion::NegatableResult> {
2022 using type = bool;
2023};
2024
2025namespace cli::conversion {
2026
2027template <std::size_t I>
2028auto get(NegatableResult& r) -> decltype(auto) {
2029 if constexpr (I == 0)
2030 return (r.name);
2031 else
2032 return (r.enabled);
2033}
2034
2035template <std::size_t I>
2036auto get(const NegatableResult& r) -> decltype(auto) {
2037 if constexpr (I == 0)
2038 return (r.name);
2039 else
2040 return (r.enabled);
2041}
2042
2043} // namespace cli::conversion
Actions that control the parser's control flow (help, exit).
Definition action.hh:1895
constexpr auto custom
Definition action.hh:2005
constexpr auto print_help
Definition action.hh:2002
constexpr auto exit_success
Definition action.hh:2003
Actions that convert a raw std::string_view token to a typed value.
Definition action.hh:635
constexpr auto choice
Definition action.hh:925
constexpr auto integer
Definition action.hh:921
constexpr auto negatable
Definition action.hh:977
constexpr auto enumeration
Definition action.hh:927
constexpr auto default_missing_value
Definition action.hh:1028
constexpr auto floating
Definition action.hh:923
auto get(NegatableResult &r) -> decltype(auto)
Definition action.hh:2028
Choice< T, Mapper > Enumeration
Definition action.hh:918
constexpr auto existing_directory
Definition action.hh:982
constexpr auto existing_file
Definition action.hh:981
constexpr auto path
Definition action.hh:980
Terminal actions that write the converted (and validated) value into argument storage.
Definition action.hh:1481
constexpr auto set_false
Definition action.hh:1874
constexpr auto insert
Definition action.hh:1881
constexpr auto reject_duplicate
Definition action.hh:1878
constexpr auto toggle
Definition action.hh:1875
constexpr auto push
Definition action.hh:1880
constexpr auto push_unique
Definition action.hh:1879
constexpr auto mark_present
Definition action.hh:1884
constexpr auto insert_or_assign
Definition action.hh:1882
constexpr auto increment
Definition action.hh:1876
constexpr auto write_to
Definition action.hh:1888
constexpr auto callback
Definition action.hh:1886
constexpr auto set_true
Definition action.hh:1873
constexpr auto set_once
Definition action.hh:1877
Actions that validate a typed value after conversion.
Definition action.hh:1046
constexpr auto non_empty
Definition action.hh:1459
constexpr auto non_negative
Definition action.hh:1458
constexpr auto predicate
Definition action.hh:1456
constexpr auto not_blank
Definition action.hh:1460
constexpr auto range
Definition action.hh:1450
constexpr auto exists
Definition action.hh:1461
constexpr auto max
Definition action.hh:1448
constexpr auto parent_exists
Definition action.hh:1464
constexpr auto matches
Definition action.hh:1454
constexpr auto one_of
Definition action.hh:1452
constexpr auto is_regular_file
Definition action.hh:1462
constexpr auto is_directory
Definition action.hh:1463
constexpr auto positive
Definition action.hh:1457
constexpr auto min
Definition action.hh:1446
Definition action.hh:29
constexpr auto ignore() -> ActionResult< T >
Helper function for constructing an ignore ActionResult with type deduction.
Definition action.hh:230
constexpr auto ok() -> ActionResult< void >
Helper function for constructing a successful ActionResult<void>.
Definition action.hh:205
ErrorKind
Broad classification of an error for structured error handling.
Definition error.hh:53
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().
Definition action.hh:618
@ invalid_value
A value could not be interpreted as the required type.
Definition error.hh:26
@ out_of_range
A numeric value fell outside the representable range of the target type.
Definition error.hh:31
@ duplicate_argument
An option or positional that forbids repetition was seen more than once.
Definition error.hh:27
@ validation_failed
A user-defined or built-in validation predicate rejected the value.
Definition error.hh:38
@ invalid_choice
A value was not among the allowed choices.
Definition error.hh:37
@ exit_success
The action pipeline requested a clean exit with code 0.
Definition error.hh:42
@ help_requested
The --help flag (or equivalent) was seen; print help and exit 0.
Definition error.hh:40
constexpr bool deduce_accepts_input
Deduces accepts_input for a non-terminal step from its operator().
Definition action.hh:588
constexpr auto operator|(Action< Fns... >, Action< Fn >)
Definition action.hh:563
constexpr auto fail(ParseError error) -> ActionResult< T >
Helper function for constructing a failure ActionResult with type deduction.
Definition action.hh:218
size_t total_values
Total values across all occurrences of this option.
Definition action.hh:275
ActionCtx(const ActionCtx< T > &other)
Constructs from a typed ActionCtx<T>, copying the counters.
Definition action.hh:284
size_t nargs
Number of values in this option occurrence.
Definition action.hh:272
size_t value_index
0-based index of current value in this occurrence.
Definition action.hh:273
size_t occurrences
Number of times the parent option token has appeared.
Definition action.hh:271
size_t index
Zero-based index into argv of the current token.
Definition action.hh:269
Context passed to each action function during pipeline execution.
Definition action.hh:246
size_t value_index
0-based index of current value in this occurrence.
Definition action.hh:251
size_t index
Zero-based index into argv of the current token.
Definition action.hh:247
size_t occurrences
Number of times the parent option token has appeared.
Definition action.hh:249
size_t nargs
Number of values in this option occurrence.
Definition action.hh:250
size_t total_values
Total values across all occurrences of this option.
Definition action.hh:253
std::reference_wrapper< T > arg
Reference to the argument's storage value.
Definition action.hh:255
constexpr auto have_value() const noexcept -> bool
Returns true when the result represents success.
Definition action.hh:138
static constexpr auto fail(ParseError e) -> ActionResult< void >
Creates a failure result carrying error.
Definition action.hh:172
constexpr auto has_error() const noexcept -> bool
Returns true when the result represents failure.
Definition action.hh:148
constexpr auto is_ignored() const noexcept -> bool
Returns true when the result represents an ignored step.
Definition action.hh:153
constexpr auto has_value() const noexcept -> bool
Returns true when the result represents success.
Definition action.hh:143
static constexpr auto ok() -> ActionResult< void >
Creates a success result.
Definition action.hh:162
static constexpr auto ignore() -> ActionResult< void >
Creates an ignore result.
Definition action.hh:181
The result type produced and consumed at each step of an action pipeline.
Definition action.hh:48
static constexpr auto ok(U &&v) -> ActionResult
Creates a success result holding value.
Definition action.hh:90
constexpr auto is_ignored() const noexcept -> bool
Returns true when the result represents an ignored step.
Definition action.hh:78
constexpr auto has_error() const noexcept -> bool
Returns true when the result represents failure.
Definition action.hh:73
constexpr auto has_value() const noexcept -> bool
Returns true when the result represents success.
Definition action.hh:68
static constexpr auto ignore() -> ActionResult
Creates an ignore result.
Definition action.hh:109
static constexpr auto fail(ParseError e) -> ActionResult
Creates a failure result carrying error.
Definition action.hh:100
ParseError error
Definition action.hh:54
constexpr auto have_value() const noexcept -> bool
Returns true when the result represents success.
Definition action.hh:63
static constexpr bool entry_is_optional
Definition action.hh:548
static constexpr auto invoke(ActionCtx< Arg > &, ActionResult< Result > input)
Definition action.hh:552
static constexpr auto validate() -> auto
Definition action.hh:556
A compile-time pipeline of action functions applied sequentially to a parsed token.
Definition action.hh:450
static constexpr auto invoke(ActionCtx< Arg > &ctx, ActionResult< Result > input)
Definition action.hh:453
static constexpr auto invoke(const ActionCtx< Arg > &ctx, ActionResult< Result > input)
Definition action.hh:475
static constexpr auto validate() -> auto
Definition action.hh:535
static constexpr bool entry_is_optional
Definition action.hh:532
static constexpr auto validate_impl()
Definition action.hh:497
Carries all context about a single parse/validation failure.
Definition error.hh:123
Wraps a user-defined callable as a non-terminal action pipeline step.
Definition action.hh:1918
typename std::invoke_result_t< decltype(Fn), ActionCtx< void > &, ActionResult< Prev > >::value_type after_type
Definition action.hh:1924
static constexpr bool accepts_input
Definition action.hh:1920
auto operator()(ActionCtx< void > ctx, ActionResult< T > input) const -> ActionResult< after_type< T > >
Definition action.hh:1933
Terminates the pipeline by returning a ParseError with code exit_success (or help_requested when prec...
Definition action.hh:1971
auto operator()(ActionCtx< Arg > ctx, ActionResult< T > input) const -> ActionResult< T >
Definition action.hh:1982
static constexpr bool accepts_input
Definition action.hh:1973
std::monostate storage_type
Definition action.hh:1979
Wraps the current value in a HelpRequested<T> sentinel type.
Definition action.hh:1946
static constexpr bool accepts_input
Definition action.hh:1948
auto operator()(ActionCtx< Arg >, ActionResult< T > input) const -> ActionResult< after_type< T > >
Definition action.hh:1957
detail::HelpRequested< Prev > after_type
Definition action.hh:1951
Converts a string token to bool.
Definition action.hh:768
static constexpr bool accepts_input
Definition action.hh:770
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< bool >
Definition action.hh:780
Converts a string token to type T via a user-supplied mapper function.
Definition action.hh:895
static constexpr bool accepts_input
Definition action.hh:897
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Definition action.hh:905
Injects a compile-time default value when an option is invoked as a flag (no value provided).
Definition action.hh:998
static constexpr bool accepts_input
Definition action.hh:1000
auto operator()(ActionCtx< void > ctx, ActionResult< std::optional< std::string_view > > input) const -> ActionResult< std::string_view >
Definition action.hh:1009
Converts a string token to std::filesystem::path, requiring the path to exist and be a directory.
Definition action.hh:858
std::filesystem::path after_type
Definition action.hh:863
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
Definition action.hh:868
static constexpr bool accepts_input
Definition action.hh:860
Converts a string token to std::filesystem::path, requiring the path to exist and be a regular file.
Definition action.hh:825
std::filesystem::path after_type
Definition action.hh:830
static constexpr bool accepts_input
Definition action.hh:827
auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
Definition action.hh:835
Converts a string token to a floating-point type T using std::from_chars.
Definition action.hh:699
static constexpr bool accepts_input
Definition action.hh:701
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Definition action.hh:711
Converts a string token to an integral type T using std::from_chars.
Definition action.hh:647
static constexpr bool accepts_input
Definition action.hh:649
constexpr auto operator()(ActionCtx< void > ctx, ActionResult< std::string_view > input) const -> ActionResult< T >
Definition action.hh:659
Result of the negatable<> conversion.
Definition action.hh:934
Converts a string token to NegatableResult by detecting a compile-time negation prefix.
Definition action.hh:950
NegatableResult after_type
Definition action.hh:957
constexpr auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< NegatableResult >
Definition action.hh:962
static constexpr bool accepts_input
Definition action.hh:952
Converts a string token to std::filesystem::path without filesystem validation.
Definition action.hh:799
static constexpr bool accepts_input
Definition action.hh:801
auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< std::filesystem::path >
Definition action.hh:811
std::filesystem::path after_type
Definition action.hh:806
Converts a string_view token to a std::string by copying.
Definition action.hh:742
std::string after_type
Definition action.hh:749
static constexpr bool accepts_input
Definition action.hh:744
constexpr auto operator()(ActionCtx< void >, ActionResult< std::string_view > input) const -> ActionResult< std::string >
Definition action.hh:754
Invokes a compile-time callable Fn with the parsed value as a terminal action.
Definition action.hh:1847
static constexpr bool accepts_input
Definition action.hh:1849
auto operator()(ActionCtx< std::monostate > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1859
std::monostate storage_type
Definition action.hh:1855
static constexpr bool accepts_input
Definition action.hh:1741
auto operator()(ActionCtx< storage_type< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1753
std::vector< std::ranges::range_value_t< detail::decay_t< Prev > > > storage_type
Definition action.hh:1749
Increments a std::size_t counter each time the option is seen.
Definition action.hh:1544
std::size_t storage_type
Definition action.hh:1552
auto operator()(ActionCtx< std::size_t > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1555
static constexpr bool accepts_input
Definition action.hh:1546
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
Definition action.hh:1721
typename StorageTypeImpl< Prev >::type storage_type
Definition action.hh:1727
auto operator()(ActionCtx< storage_type< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1730
static constexpr bool accepts_input
Definition action.hh:1710
Inserts the value into a std::set<T> (duplicates are silently ignored by the set).
Definition action.hh:1687
auto operator()(ActionCtx< std::set< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1700
std::set< detail::decay_t< Prev > > storage_type
Definition action.hh:1697
static constexpr bool accepts_input
Definition action.hh:1689
Sets a bool flag to true when the option is seen, regardless of value.
Definition action.hh:1771
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1782
static constexpr bool accepts_input
Definition action.hh:1773
Appends the value to a std::vector<T>, rejecting duplicate values.
Definition action.hh:1630
std::vector< detail::decay_t< Prev > > storage_type
Definition action.hh:1639
auto operator()(ActionCtx< std::vector< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1642
static constexpr bool accepts_input
Definition action.hh:1632
Appends the value to a std::vector<T>.
Definition action.hh:1661
auto operator()(ActionCtx< std::vector< detail::decay_t< T > > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1673
std::vector< detail::decay_t< Prev > > storage_type
Definition action.hh:1670
static constexpr bool accepts_input
Definition action.hh:1663
Passes the value through unchanged, but returns ErrorCode::duplicate_argument if the option has been ...
Definition action.hh:1570
auto operator()(ActionCtx< void > ctx, ActionResult< T > input) const -> ActionResult< T >
Definition action.hh:1582
static constexpr bool accepts_input
Definition action.hh:1572
Sets the bool storage to false.
Definition action.hh:1504
static constexpr bool accepts_input
Definition action.hh:1506
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1515
Stores the input value and rejects subsequent occurrences.
Definition action.hh:1600
auto operator()(ActionCtx< detail::decay_t< T > > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1612
static constexpr bool accepts_input
Definition action.hh:1602
detail::decay_t< Prev > storage_type
Definition action.hh:1609
Sets the bool storage to true.
Definition action.hh:1484
static constexpr bool accepts_input
Definition action.hh:1486
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1495
Writes the value directly into an external variable via a T* pointer.
Definition action.hh:1800
auto operator()(ActionCtx< T * > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1810
static constexpr bool accepts_input
Definition action.hh:1802
Flips the bool storage each time the option is seen.
Definition action.hh:1524
static constexpr bool accepts_input
Definition action.hh:1526
auto operator()(ActionCtx< bool > ctx, ActionResult< T > input) const -> ActionResult< void >
Definition action.hh:1535
Validates that a std::filesystem::path exists on the filesystem.
Definition action.hh:1313
static constexpr bool accepts_input
Definition action.hh:1315
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1325
Validates that a std::filesystem::path refers to a directory.
Definition action.hh:1361
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1373
static constexpr bool accepts_input
Definition action.hh:1363
Validates that a std::filesystem::path refers to a regular file.
Definition action.hh:1337
static constexpr bool accepts_input
Definition action.hh:1339
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1349
Validates that the input string matches a compile-time regular expression.
Definition action.hh:1288
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1299
static constexpr bool accepts_input
Definition action.hh:1290
Validates that the input value is less than or equal to MaxValue.
Definition action.hh:1084
static constexpr bool accepts_input
Definition action.hh:1086
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1096
Validates that the input value is greater than or equal to MinValue.
Definition action.hh:1054
static constexpr bool accepts_input
Definition action.hh:1056
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1066
Validates that the input value is not empty (requires .empty() member).
Definition action.hh:1194
static constexpr bool accepts_input
Definition action.hh:1196
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1206
Validates that the input value is non-negative (>= 0).
Definition action.hh:1168
static constexpr bool accepts_input
Definition action.hh:1170
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1180
Validates that the input string is not blank (not all whitespace).
Definition action.hh:1219
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1232
static constexpr bool accepts_input
Definition action.hh:1221
Validates that the input value equals one of the compile-time constants Allowed.
Definition action.hh:1252
static constexpr bool accepts_input
Definition action.hh:1254
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1266
Validates that the parent directory of a std::filesystem::path exists.
Definition action.hh:1386
static constexpr bool accepts_input
Definition action.hh:1388
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1398
Validates that the input value is strictly positive (> 0).
Definition action.hh:1143
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1155
static constexpr bool accepts_input
Definition action.hh:1145
Validates a value using a user-supplied compile-time predicate.
Definition action.hh:1419
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1433
static constexpr bool accepts_input
Definition action.hh:1421
Validates that the input value is within the closed interval [MinValue, MaxValue].
Definition action.hh:1117
auto operator()(ActionCtx< void > ctx, ActionResult< U > input) const -> ActionResult< U >
Definition action.hh:1129
static constexpr bool accepts_input
Definition action.hh:1119