27 template<concepts::Object Base>
30 constexpr static auto max_options = 100ZU;
31 constexpr static auto max_arguments = 100ZU;
35 : m_app_name(app_name), m_description(description) {}
38 requires(concepts::MemberObjectPointer<
decltype(member)> &&
40 constexpr auto option(Optional<char> short_name, Optional<TransparentStringView> long_name,
41 StringView description,
bool required =
false,
bool always_succeed =
false) && {
42 auto new_option = Option {
c_<member>, short_name, long_name, description, required, always_succeed };
43 DI_ASSERT(m_options.push_back(new_option));
44 return di::move(*
this);
47 constexpr auto help(Optional<char> short_name = {}, Optional<TransparentStringView> long_name =
"help"_tsv,
48 StringView description =
"Print help message"_sv) {
51 "A help message requires the argument type to have a boolean help member.");
53 "A help message requires the argument type to have a boolean help member.");
54 return di::move(*this).template option<&Base::help>(short_name, long_name, description,
false,
true);
58 requires(concepts::MemberObjectPointer<
decltype(member)> &&
61 auto new_argument = Argument {
c_<member>, name, description, required };
62 DI_ASSERT(m_arguments.push_back(new_argument));
63 return di::move(*
this);
68 using namespace di::string_literals;
71 return Unexpected(BasicError::InvalidArgument);
73 args = *args.subspan(1);
75 auto seen_arguments = Array<bool, max_options> {};
76 seen_arguments.fill(
false);
78 auto count_option_processed =
usize { 0 };
80 auto send_arg_to_back = [&](
usize i) {
82 count_option_processed++;
85 auto result = Base {};
86 for (
usize i = 0; i < args.size(); i++) {
87 auto arg_index = i - count_option_processed;
88 auto arg = args[arg_index];
91 if (!arg.starts_with(
'-')) {
96 if (arg ==
"--"_tsv) {
97 send_arg_to_back(arg_index);
102 if (!arg.starts_with(
"--"_tsv)) {
103 for (
usize char_index = 1; char_index < arg.size(); char_index++) {
104 auto index = lookup_short_name(arg[char_index]);
106 return Unexpected(BasicError::InvalidArgument);
110 if (option_boolean(*index)) {
111 DI_TRY(option_parse(*index, seen_arguments, &result, {}));
112 if (option_always_succeeds(*index)) {
119 auto value_view = arg.substr(arg.begin() +
isize(char_index + 1));
120 if (!value_view.empty()) {
121 DI_TRY(option_parse(*index, seen_arguments, &result, value_view));
122 if (option_always_succeeds(*index)) {
129 if (i + 1 >= args.size()) {
130 return Unexpected(BasicError::InvalidArgument);
134 DI_TRY(option_parse(*index, seen_arguments, &result, args[arg_index + 1]));
135 if (option_always_succeeds(*index)) {
138 send_arg_to_back(arg_index);
141 send_arg_to_back(arg_index);
146 auto equal = arg.find(
'=');
149 name = arg.substr(arg.begin() + 2);
151 name = arg.substr(arg.begin() + 2,
equal.begin());
154 auto index = lookup_long_name(name);
156 return Unexpected(BasicError::InvalidArgument);
159 if (option_boolean(*index)) {
161 return Unexpected(BasicError::InvalidArgument);
163 DI_TRY(option_parse(*index, seen_arguments, &result, {}));
164 if (option_always_succeeds(*index)) {
167 send_arg_to_back(arg_index);
172 if (!
equal && i + 1 >= args.size()) {
173 return Unexpected(BasicError::InvalidArgument);
176 value = args[arg_index + 1];
177 send_arg_to_back(arg_index);
178 send_arg_to_back(arg_index);
182 send_arg_to_back(arg_index);
185 DI_TRY(option_parse(*index, seen_arguments, &result, value));
186 if (option_always_succeeds(*index)) {
192 for (
usize i = 0; i < m_options.size(); i++) {
193 if (!seen_arguments[i] && option_required(i)) {
194 return Unexpected(BasicError::InvalidArgument);
199 auto positional_arguments = *args.subspan(0, args.size() - count_option_processed);
200 if (positional_arguments.size() < minimum_required_argument_count()) {
201 return Unexpected(BasicError::InvalidArgument);
204 auto argument_index =
usize(0);
205 for (
auto i =
usize(0); i < positional_arguments.size(); argument_index++) {
206 auto count_to_consume = !argument_variadic(i) ? 1 : positional_arguments.size() - argument_count() + 1;
207 auto input = *positional_arguments.subspan(i, count_to_consume);
208 DI_TRY(argument_parse(argument_index, &result, input));
209 i += count_to_consume;
214 template<Impl<io::Writer> Writer>
215 constexpr void write_help(
Writer& writer)
const {
216 using Enc = container::string::Utf8Encoding;
218 constexpr auto header_effect = di::FormatEffect::Bold | di::FormatColor::Yellow;
219 constexpr auto program_effect = di::FormatEffect::Bold;
220 constexpr auto option_effect = di::FormatColor::Cyan;
221 constexpr auto option_value_effect = di::FormatColor::Green;
222 constexpr auto argument_effect = di::FormatColor::Green;
232 for (
auto const& option : m_options) {
233 if (option.required()) {
236 if (!option.boolean()) {
241 for (
auto const& argument : m_arguments) {
246 if (!m_arguments.empty()) {
248 for (
auto const& argument : m_arguments) {
250 argument.description());
254 if (!m_options.empty()) {
256 for (
auto const& option : m_options) {
258 if (option.short_name()) {
260 if (!option.boolean() && !option.long_name()) {
264 if (option.short_name() && option.long_name()) {
267 if (option.long_name()) {
269 if (!option.boolean()) {
278 constexpr auto help_string()
const {
279 auto writer = di::StringWriter {};
281 return di::move(writer).output();
285 constexpr auto option_required(
usize index)
const ->
bool {
return m_options[index].required(); }
286 constexpr auto option_boolean(
usize index)
const ->
bool {
return m_options[index].boolean(); }
287 constexpr auto option_always_succeeds(
usize index)
const ->
bool {
return m_options[index].always_succeeds(); }
289 constexpr auto option_parse(
usize index, Span<bool> seen_arguments, Base* output,
290 Optional<TransparentStringView> input)
const ->
Result<void> {
292 seen_arguments[index] =
true;
296 constexpr auto argument_variadic(
usize index)
const ->
bool {
return m_arguments[index].variadic(); }
298 constexpr auto argument_parse(
usize index, Base* output, Span<TransparentStringView> input)
const
300 return m_arguments[index].parse(output, input);
303 constexpr auto minimum_required_argument_count() const ->
usize {
304 return di::sum(m_arguments | di::transform(&Argument::required_argument_count));
307 constexpr auto argument_count() const ->
usize {
return m_arguments.size(); }
309 constexpr auto lookup_short_name(
char short_name)
const -> Optional<usize> {
310 auto const* it =
di::find(m_options, short_name, &Option::short_name);
311 return lift_bool(it != m_options.end()) % [&] {
312 return usize(it - m_options.begin());
317 auto const* it =
di::find(m_options, long_name, &Option::long_name);
318 return lift_bool(it != m_options.end()) % [&] {
319 return usize(it - m_options.begin());
325 StaticVector<Option, Constexpr<max_options>> m_options;
326 StaticVector<Argument, Constexpr<max_arguments>> m_arguments;
330template<concepts::Object T>
332 return detail::Parser<T> { app_name, description };
#define DI_ASSERT(...)
Definition assert_bool.h:7
#define DI_TRY(...)
Definition monad_try.h:13
constexpr auto cli_parser(StringView app_name, StringView description)
Definition parser.h:331
string::StringViewImpl< string::Utf8Encoding > StringView
Definition string_view.h:12
constexpr auto rotate
Definition rotate.h:94
string::StringViewImpl< string::TransparentEncoding > TransparentStringView
Definition string_view.h:13
constexpr auto value
Definition value.h:34
constexpr auto writer_print
Definition writer_print.h:20
meta::List< WriteSome, Flush, InteractiveDevice > Writer
Definition writer.h:85
constexpr auto writer_println
Definition writer_println.h:21
ssize_t isize
Definition integers.h:34
size_t usize
Definition integers.h:33
Expected< T, Error > Result
Definition result.h:8
Unexpected(E &&) -> Unexpected< meta::UnwrapRefDecay< E > >
Definition any_storable.h:9
constexpr auto find
Definition find.h:35
constexpr auto c_
A value of type Constexpr<val>.
Definition constexpr.h:252
constexpr auto not_fn(F &&function)
Definition not_fn.h:55
constexpr auto equal
Definition equal.h:23
constexpr auto lift_bool
Definition lift_bool.h:13
constexpr auto sum
Definition sum.h:26
constexpr auto any_of
Definition any_of.h:24
constexpr auto parse
Definition parse.h:23