cli20 0.1.2
A C++20 typed-schema command-line parser
Loading...
Searching...
No Matches
constraint.hh
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <optional>
5#include <string>
6#include <string_view>
7
8#include "cli/error.hh"
9
10namespace cli {
11
30 std::optional<ParseError> error;
31
32 [[nodiscard]] auto ok() const noexcept -> bool { return !error.has_value(); }
33 [[nodiscard]] explicit operator bool() const noexcept { return ok(); }
34
35 static auto pass() -> ConstraintResult { return {}; }
36 static auto fail(ParseError e) -> ConstraintResult { return {std::move(e)}; }
37
40 return a.ok() ? std::move(b) : std::move(a);
41 }
42
45 return a.ok() ? std::move(a) : std::move(b);
46 }
47};
48
49namespace detail {
50
51template <class T>
52concept NamedProviderArg = requires(const T& t) {
53 { t.provided() } -> std::convertible_to<bool>;
54 {
55 std::remove_cvref_t<T>::name.view()
56 } -> std::convertible_to<std::string_view>;
57};
58
59template <NamedProviderArg... Args>
60auto names_list(const Args&...) -> std::string {
61 std::string s;
62 bool first = true;
63 (..., (s += (first ? std::string{} : std::string{", "}) + "--" +
64 std::string(std::remove_cvref_t<Args>::name.view()),
65 first = false));
66 return s;
67}
68
69} // namespace detail
70
84namespace constraint {
85
92template <detail::NamedProviderArg... Args>
93auto one_of(const Args&... args) -> ConstraintResult {
94 int count = (0 + ... + (args.provided() ? 1 : 0));
95 if (count == 1) return ConstraintResult::pass();
96 auto subject = detail::names_list(args...);
97 if (count == 0) {
100 .kind = ErrorKind::validation,
101 .subject = subject,
102 .detail = "exactly one of these options must be provided",
103 });
104 }
107 .kind = ErrorKind::validation,
108 .subject = subject,
109 .detail = "exactly one of these options must be provided, but multiple "
110 "were given",
111 });
112}
113
119template <detail::NamedProviderArg... Args>
120auto at_most_one_of(const Args&... args) -> ConstraintResult {
121 int count = (0 + ... + (args.provided() ? 1 : 0));
122 if (count <= 1) return ConstraintResult::pass();
125 .kind = ErrorKind::validation,
126 .subject = detail::names_list(args...),
127 .detail = "at most one of these options may be provided",
128 });
129}
130
136template <detail::NamedProviderArg... Args>
137auto all_of(const Args&... args) -> ConstraintResult {
138 bool all = (... && args.provided());
139 if (all) return ConstraintResult::pass();
142 .kind = ErrorKind::validation,
143 .subject = detail::names_list(args...),
144 .detail = "all of these options must be provided",
145 });
146}
147
153template <detail::NamedProviderArg... Args>
154auto all_or_none(const Args&... args) -> ConstraintResult {
155 int count = (0 + ... + (args.provided() ? 1 : 0));
156 constexpr int total = static_cast<int>(sizeof...(Args));
157 if (count == 0 || count == total) return ConstraintResult::pass();
160 .kind = ErrorKind::validation,
161 .subject = detail::names_list(args...),
162 .detail = "either all of these options must be provided, or none",
163 });
164}
165
172template <detail::NamedProviderArg Cond, detail::NamedProviderArg Req>
173auto requires_if(const Cond& condition, const Req& required)
175 if (!condition.provided() || required.provided())
176 return ConstraintResult::pass();
179 .kind = ErrorKind::validation,
180 .subject = "--" + std::string(std::remove_cvref_t<Req>::name.view()),
181 .detail = "required when --" +
182 std::string(std::remove_cvref_t<Cond>::name.view()) +
183 " is provided",
184 });
185}
186
187} // namespace constraint
188
189} // namespace cli
Post-parse constraint helpers.
Definition constraint.hh:84
auto one_of(const Args &... args) -> ConstraintResult
Exactly one of the listed options must be provided.
Definition constraint.hh:93
auto at_most_one_of(const Args &... args) -> ConstraintResult
At most one of the listed options may be provided.
auto all_of(const Args &... args) -> ConstraintResult
All of the listed options must be provided.
auto requires_if(const Cond &condition, const Req &required) -> ConstraintResult
If condition is provided, required must also be provided.
auto all_or_none(const Args &... args) -> ConstraintResult
Either all of the listed options must be provided, or none.
Definition action.hh:29
constexpr auto ok() -> ActionResult< void >
Helper function for constructing a successful ActionResult<void>.
Definition action.hh:205
constexpr auto required
Convenience constant for Presence::required.
@ dependency_missing
An option that depends on another option was given without it.
Definition error.hh:35
@ mutually_exclusive
Two mutually-exclusive options were both supplied.
Definition error.hh:34
@ missing_required
A required option or positional was not provided.
Definition error.hh:33
The result type returned by a constraints() post-parse hook.
Definition constraint.hh:29
friend auto operator&&(ConstraintResult a, ConstraintResult b) -> ConstraintResult
Definition constraint.hh:38
static auto pass() -> ConstraintResult
Definition constraint.hh:35
friend auto operator||(ConstraintResult a, ConstraintResult b) -> ConstraintResult
Definition constraint.hh:43
auto ok() const noexcept -> bool
Definition constraint.hh:32
std::optional< ParseError > error
Definition constraint.hh:30
static auto fail(ParseError e) -> ConstraintResult
Definition constraint.hh:36
Carries all context about a single parse/validation failure.
Definition error.hh:123