@@ -42,56 +42,6 @@ struct ArgParser {
42
42
bool valid () const { return cursor < args.size (); }
43
43
};
44
44
45
- template <std::meta::info R, typename T>
46
- void validate (T value, std::string_view parent) {
47
- if constexpr (erl::meta::has_annotation (R, ^^erl::annotations::Expect)) {
48
- constexpr static auto constraint = [:value_of (erl::meta::get_annotation (
49
- R,
50
- ^^erl::annotations::Expect)):];
51
-
52
- std::vector<std::string> failed_terms;
53
- if (constraint.eval_verbose (erl::Tuple{value}, failed_terms)) {
54
- return ;
55
- }
56
-
57
- std::println (" Validation failed for argument `{}` of `{}`" , identifier_of (R), parent);
58
- for (auto && term : failed_terms | std::views::reverse) {
59
- std::println (" => {}{}{} evaluated to {}false{}" ,
60
- fg::Blue,
61
- term,
62
- style::Reset,
63
- fg::Red,
64
- style::Reset);
65
- }
66
- std::exit (1 );
67
- }
68
- }
69
-
70
- struct ArgumentInfo {
71
- char const * name;
72
- char const * type;
73
- char const * description;
74
- bool optional = false ;
75
- std::size_t constraint_count;
76
- char const * const * constraints;
77
-
78
- explicit consteval ArgumentInfo (std::meta::info reflection)
79
- : name(std::meta::define_static_string(identifier_of(reflection)))
80
- , type(std::meta::define_static_string(
81
- display_string_of (type_of(reflection)))) // TODO clean at this point?
82
- , optional(is_function_parameter(reflection) ? has_default_argument(reflection)
83
- : has_default_member_initializer(reflection)) {
84
- if (auto desc = annotation_of_type<annotations::Description>(reflection); desc) {
85
- description = desc->data ;
86
- } else {
87
- description = std::meta::define_static_string (" " );
88
- }
89
-
90
- constraint_count = 0 ;
91
- constraints = nullptr ; // std::meta::define_static_array(std::vector{""}).data();
92
- }
93
- };
94
-
95
45
struct Argument {
96
46
struct Unevaluated {
97
47
using handler_type = void (Unevaluated::*)(void *) const ;
@@ -101,14 +51,40 @@ struct Argument {
101
51
102
52
void operator ()(void * args) const { (this ->*handle)(args); }
103
53
54
+ template <std::meta::info R, typename T>
55
+ void validate (T value) const {
56
+ auto parent = display_string_of (parent_of (R));
57
+ if constexpr (erl::meta::has_annotation (R, ^^erl::annotations::Expect)) {
58
+ constexpr static auto constraint = [:value_of (erl::meta::get_annotation (
59
+ R,
60
+ ^^erl::annotations::Expect)):];
61
+
62
+ std::vector<std::string> failed_terms;
63
+ if (constraint.eval_verbose (erl::Tuple{value}, failed_terms)) {
64
+ return ;
65
+ }
66
+
67
+ std::println (" Validation failed for argument `{}` of `{}`" , identifier_of (R), parent);
68
+ for (auto && term : failed_terms | std::views::reverse) {
69
+ std::println (" => {}{}{} evaluated to {}false{}" ,
70
+ fg::Blue,
71
+ term,
72
+ style::Reset,
73
+ fg::Red,
74
+ style::Reset);
75
+ }
76
+ std::exit (1 );
77
+ }
78
+ }
79
+
104
80
template <std::size_t Idx, std::meta::info R>
105
81
void do_handle (void * arguments) const {
106
82
using parent = [:is_function_parameter (R) ? type_of (parent_of (R)) : parent_of (R):];
107
83
using arg_tuple = ArgumentTuple<parent>;
108
84
109
85
using type = [:remove_cvref (type_of (R)):];
110
86
auto value = parse_value<type>(argument);
111
- validate<R>(value, display_string_of ( parent_of (R)) );
87
+ validate<R>(value);
112
88
get<Idx>(*static_cast <arg_tuple*>(arguments)) = value;
113
89
}
114
90
@@ -140,22 +116,46 @@ struct Argument {
140
116
};
141
117
142
118
using parser_type = bool (Unevaluated::*)(ArgParser&);
143
- // using default_type = void (*)(ArgParser const&, ArgumentTuple<T> const&);
144
-
145
119
std::size_t index;
146
120
parser_type parse;
147
- // default_type check_default;
148
- ArgumentInfo info;
121
+
122
+ char const * name;
123
+ char const * type;
124
+ char const * description;
125
+ bool optional = false ;
126
+ char const * constraint;
127
+
128
+ template <std::meta::info R, auto Constraint>
129
+ constexpr char const * stringify_constraint () {
130
+ std::string _constraint = Constraint.to_string (identifier_of (R));
131
+ return std::meta::define_static_string (_constraint.substr (1 , _constraint.size () - 2 ));
132
+ }
149
133
150
134
consteval Argument (std::size_t idx, std::meta::info reflection)
151
135
: index(idx)
152
136
, parse(meta::instantiate<parser_type>(^^Unevaluated::template do_parse,
153
137
std::meta::reflect_value (idx),
154
138
reflect_value(reflection)))
155
- // , check_default(meta::instantiate<default_type>(^^check_default_initializable,
156
- // ^^T,
157
- // std::meta::reflect_value(idx)))
158
- , info(reflection) {}
139
+ , name(std::meta::define_static_string(identifier_of(reflection)))
140
+ , type(std::meta::define_static_string(
141
+ display_string_of (type_of(reflection)))) // TODO clean at this point?
142
+ , optional(is_function_parameter(reflection) ? has_default_argument(reflection)
143
+ : has_default_member_initializer(reflection)) {
144
+ if (auto desc = annotation_of_type<annotations::Description>(reflection); desc) {
145
+ description = desc->data ;
146
+ } else {
147
+ description = std::meta::define_static_string (" " );
148
+ }
149
+
150
+ if (erl::meta::has_annotation (reflection, ^^erl::annotations::Expect)) {
151
+ auto annotation = erl::meta::get_annotation (reflection, ^^erl::annotations::Expect);
152
+ constraint = (this ->*meta::instantiate<char const * (Argument::*)()>(^^stringify_constraint,
153
+ reflect_value (reflection),
154
+ value_of (annotation)))();
155
+ } else {
156
+ constraint = nullptr ;
157
+ }
158
+ }
159
159
};
160
160
161
161
template <typename T>
@@ -208,8 +208,8 @@ struct Option {
208
208
if ((argument.*args...[Idx].parse )(parser)) {
209
209
arguments.push_back (argument);
210
210
} else {
211
- if (!args...[Idx].info . optional ) {
212
- parser.fail (" Missing argument {} of option {}" , args...[Idx].info . name , arg_name);
211
+ if (!args...[Idx].optional ) {
212
+ parser.fail (" Missing argument {} of option {}" , args...[Idx].name , arg_name);
213
213
}
214
214
}
215
215
};
@@ -226,7 +226,7 @@ struct Option {
226
226
char const * description = nullptr ;
227
227
228
228
std::size_t argument_count;
229
- ArgumentInfo const * arguments;
229
+ Argument const * arguments;
230
230
231
231
consteval Option (std::string_view name, std::meta::info reflection)
232
232
: name(std::meta::define_static_string(name)) {
@@ -243,13 +243,8 @@ struct Option {
243
243
}
244
244
}
245
245
246
- std::vector<ArgumentInfo> arg_infos;
247
- for (auto arg : args) {
248
- arg_infos.push_back (arg.info );
249
- }
250
-
251
246
argument_count = args.size ();
252
- arguments = std::meta::define_static_array (arg_infos ).data ();
247
+ arguments = std::meta::define_static_array (args ).data ();
253
248
254
249
std::vector parse_args = {meta::promote (name), reflect_value (reflection)};
255
250
for (auto arg : args) {
@@ -268,10 +263,10 @@ class Spec {
268
263
private:
269
264
consteval auto get_arguments () {
270
265
std::vector<Argument> fields;
271
- auto members = nonstatic_data_members_of (^^T) ;
272
- for (std:: size_t idx = 0 ; idx < members. size (); ++idx ) {
273
- if (!meta::has_annotation<annotations::Option>(members[idx] )) {
274
- fields.emplace_back (idx, members[idx] );
266
+ std:: size_t idx = 0 ;
267
+ for (auto member : nonstatic_data_members_of (^^T) ) {
268
+ if (!meta::has_annotation<annotations::Option>(member )) {
269
+ fields.emplace_back (idx++, member );
275
270
}
276
271
}
277
272
return fields;
@@ -364,30 +359,61 @@ struct clap {
364
359
auto program_name = Program::name ();
365
360
std::string arguments{};
366
361
for (auto argument : spec.arguments ) {
367
- if (argument.info . optional ) {
368
- arguments += std::format (" [{}] " , argument.info . name );
362
+ if (argument.optional ) {
363
+ arguments += std::format (" [{}] " , argument.name );
369
364
} else {
370
- arguments += std::format (" {} " , argument.info . name );
365
+ arguments += std::format (" {} " , argument.name );
371
366
}
372
367
}
373
368
std::println (" usage: {} {}" , program_name, arguments);
374
- std::println (" Options:" );
369
+
370
+ if (auto desc = annotation_of_type<annotations::Description>(^^T); desc) {
371
+ std::println (" \n {}" , desc->data );
372
+ }
373
+
374
+ std::println (" \n Arguments:" );
375
+ for (auto argument : spec.arguments ) {
376
+ std::string constraint = argument.constraint ? std::string (" requires: " ) + argument.constraint : " " ;
377
+ std::println (" {} {}{}" , argument.type , argument.name , constraint);
378
+ }
379
+
380
+ std::println (" \n Options:" );
375
381
376
382
auto name_length = [](Option<T> opt) { return std::strlen (opt.name ); };
377
- std::size_t max_name_length = std::max (
378
- std::ranges::max (spec.commands | std::views::transform (name_length)),
379
- std::ranges::max (spec.options | std::views::transform (name_length))
380
- );
383
+ std::size_t max_name_length =
384
+ std::max (std::ranges::max (spec.commands | std::views::transform (name_length)),
385
+ std::ranges::max (spec.options | std::views::transform (name_length)));
381
386
382
- auto print_option = [&](auto opt) {
387
+ auto print_option = [&](Option<T> opt) {
383
388
std::string params;
389
+ bool has_constraints = false ;
384
390
for (std::size_t idx = 0 ; idx < opt.argument_count ; ++idx) {
385
- params += std::format (" {}[{}] " , opt.arguments [idx].name , opt.arguments [idx].type );
391
+ if (opt.arguments [idx].optional ) {
392
+ params += std::format (" [{}] " , opt.arguments [idx].name );
393
+ } else {
394
+ params += std::format (" {} " , opt.arguments [idx].name );
395
+ }
396
+ if (opt.arguments [idx].constraint != nullptr ) {
397
+ has_constraints = true ;
398
+ }
386
399
}
387
400
388
- std::println (" --{} {}" , opt.name , params);
401
+ std::println (" --{} {}" , opt.name , params);
402
+ std::size_t offset = max_name_length + 4 + 4 ;
389
403
if (opt.description ) {
390
- std::println (" {:<{}} {}" , " " , max_name_length, opt.description );
404
+ std::println (" {:<{}}{}\n " , " " , offset, opt.description );
405
+ }
406
+
407
+ if (opt.argument_count != 0 ) {
408
+ std::println (" {:<{}}Arguments:" , " " , offset);
409
+ for (std::size_t idx = 0 ; idx < opt.argument_count ; ++idx) {
410
+ std::string constraint = opt.arguments [idx].constraint ? std::string (" requires: " ) + opt.arguments [idx].constraint : " " ;
411
+ char const * optional = " " ;
412
+ if (opt.arguments [idx].optional ) {
413
+ optional = " (optional)" ;
414
+ }
415
+ std::println (" {:<{}}{} {}{}{}" , " " , offset + 4 , opt.arguments [idx].type , opt.arguments [idx].name , optional, constraint);
416
+ }
391
417
}
392
418
};
393
419
@@ -448,8 +474,8 @@ T parse_args(std::vector<std::string_view> args_in) {
448
474
}
449
475
450
476
for (auto arg : spec.arguments | std::views::drop (parsed_args.size ())) {
451
- if (!arg.info . optional ){
452
- parser.fail (" Missing required argument `{}`" , arg.info . name );
477
+ if (!arg.optional ) {
478
+ parser.fail (" Missing required argument `{}`" , arg.name );
453
479
}
454
480
}
455
481
0 commit comments