Skip to content

Commit 1dca019

Browse files
committed
refactor
1 parent ef45957 commit 1dca019

File tree

7 files changed

+193
-115
lines changed

7 files changed

+193
-115
lines changed

example/args.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <erl/config/args.hpp>
22
#include <erl/print.hpp>
33

4-
struct Args : erl::clap {
4+
struct [[= erl::clap::description("Example program.")]] Args : erl::clap {
55
std::string text;
66
[[=expect(2 < value && value < 10)]]
77
int times = 5;

include/erl/config/annotations.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ struct Description : StringAnnotation {
2727
};
2828

2929
template <typename T>
30-
struct Expect : T {};
30+
struct Expect : T {
31+
constexpr std::string to_string(std::string_view replacement = "value") const {
32+
return T::to_string(std::vector{replacement});
33+
}
34+
};
3135

3236
struct {
3337
consteval auto operator()(auto expr) const {

include/erl/config/args.hpp

Lines changed: 112 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -42,56 +42,6 @@ struct ArgParser {
4242
bool valid() const { return cursor < args.size(); }
4343
};
4444

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-
9545
struct Argument {
9646
struct Unevaluated {
9747
using handler_type = void (Unevaluated::*)(void*) const;
@@ -101,14 +51,40 @@ struct Argument {
10151

10252
void operator()(void* args) const { (this->*handle)(args); }
10353

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+
10480
template <std::size_t Idx, std::meta::info R>
10581
void do_handle(void* arguments) const {
10682
using parent = [:is_function_parameter(R) ? type_of(parent_of(R)) : parent_of(R):];
10783
using arg_tuple = ArgumentTuple<parent>;
10884

10985
using type = [:remove_cvref(type_of(R)):];
11086
auto value = parse_value<type>(argument);
111-
validate<R>(value, display_string_of(parent_of(R)));
87+
validate<R>(value);
11288
get<Idx>(*static_cast<arg_tuple*>(arguments)) = value;
11389
}
11490

@@ -140,22 +116,46 @@ struct Argument {
140116
};
141117

142118
using parser_type = bool (Unevaluated::*)(ArgParser&);
143-
// using default_type = void (*)(ArgParser const&, ArgumentTuple<T> const&);
144-
145119
std::size_t index;
146120
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+
}
149133

150134
consteval Argument(std::size_t idx, std::meta::info reflection)
151135
: index(idx)
152136
, parse(meta::instantiate<parser_type>(^^Unevaluated::template do_parse,
153137
std::meta::reflect_value(idx),
154138
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+
}
159159
};
160160

161161
template <typename T>
@@ -208,8 +208,8 @@ struct Option {
208208
if ((argument.*args...[Idx].parse)(parser)) {
209209
arguments.push_back(argument);
210210
} 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);
213213
}
214214
}
215215
};
@@ -226,7 +226,7 @@ struct Option {
226226
char const* description = nullptr;
227227

228228
std::size_t argument_count;
229-
ArgumentInfo const* arguments;
229+
Argument const* arguments;
230230

231231
consteval Option(std::string_view name, std::meta::info reflection)
232232
: name(std::meta::define_static_string(name)) {
@@ -243,13 +243,8 @@ struct Option {
243243
}
244244
}
245245

246-
std::vector<ArgumentInfo> arg_infos;
247-
for (auto arg : args) {
248-
arg_infos.push_back(arg.info);
249-
}
250-
251246
argument_count = args.size();
252-
arguments = std::meta::define_static_array(arg_infos).data();
247+
arguments = std::meta::define_static_array(args).data();
253248

254249
std::vector parse_args = {meta::promote(name), reflect_value(reflection)};
255250
for (auto arg : args) {
@@ -268,10 +263,10 @@ class Spec {
268263
private:
269264
consteval auto get_arguments() {
270265
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);
275270
}
276271
}
277272
return fields;
@@ -364,30 +359,61 @@ struct clap {
364359
auto program_name = Program::name();
365360
std::string arguments{};
366361
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);
369364
} else {
370-
arguments += std::format("{} ", argument.info.name);
365+
arguments += std::format("{} ", argument.name);
371366
}
372367
}
373368
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("\nArguments:");
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("\nOptions:");
375381

376382
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)));
381386

382-
auto print_option = [&](auto opt) {
387+
auto print_option = [&](Option<T> opt) {
383388
std::string params;
389+
bool has_constraints = false;
384390
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+
}
386399
}
387400

388-
std::println("--{} {}", opt.name, params);
401+
std::println(" --{} {}", opt.name, params);
402+
std::size_t offset = max_name_length + 4 + 4;
389403
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+
}
391417
}
392418
};
393419

@@ -448,8 +474,8 @@ T parse_args(std::vector<std::string_view> args_in) {
448474
}
449475

450476
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);
453479
}
454480
}
455481

0 commit comments

Comments
 (0)