55#include < charconv>
66#include < concepts>
77#include < cstdlib>
8- #include < format >
8+ #include < expected >
99#include < memory>
1010#include < optional>
1111#include < span>
1717
1818#include " text.h"
1919#include " trait.h"
20+ #include " eventide/serde/serde/spelling.h"
2021
2122namespace deco ::decl {
2223
@@ -300,6 +301,69 @@ struct IntoContext {
300301 }
301302};
302303
304+ using AliasForwardResult = std::expected<std::vector<std::string>, std::string>;
305+ using AliasForwardFn = AliasForwardResult (*)(const backend::ParsedArgumentOwning& arg);
306+ using AliasForwardFnWithContext = AliasForwardResult (*)(const backend::ParsedArgumentOwning& arg,
307+ const IntoContext& context);
308+
309+ struct AliasForwardField {
310+ enum class Kind : char {
311+ None = 0 ,
312+ Static = 1 ,
313+ Dynamic = 2 ,
314+ DynamicWithContext = 3 ,
315+ };
316+
317+ Kind kind = Kind::None;
318+ std::vector<std::string_view> static_tokens;
319+ AliasForwardFn dynamic = nullptr ;
320+ AliasForwardFnWithContext dynamic_with_context = nullptr ;
321+
322+ constexpr AliasForwardField () = default;
323+
324+ constexpr auto operator =(std::initializer_list<std::string_view> tokens) -> AliasForwardField& {
325+ kind = Kind::Static;
326+ static_tokens.assign (tokens.begin (), tokens.end ());
327+ dynamic = nullptr ;
328+ dynamic_with_context = nullptr ;
329+ return *this ;
330+ }
331+
332+ constexpr auto operator =(std::vector<std::string_view> tokens) -> AliasForwardField& {
333+ kind = Kind::Static;
334+ static_tokens = std::move (tokens);
335+ dynamic = nullptr ;
336+ dynamic_with_context = nullptr ;
337+ return *this ;
338+ }
339+
340+ constexpr auto operator =(AliasForwardFn fn) -> AliasForwardField& {
341+ kind = fn ? Kind::Dynamic : Kind::None;
342+ static_tokens.clear ();
343+ dynamic = fn;
344+ dynamic_with_context = nullptr ;
345+ return *this ;
346+ }
347+
348+ constexpr auto operator =(AliasForwardFnWithContext fn) -> AliasForwardField& {
349+ kind = fn ? Kind::DynamicWithContext : Kind::None;
350+ static_tokens.clear ();
351+ dynamic = nullptr ;
352+ dynamic_with_context = fn;
353+ return *this ;
354+ }
355+
356+ constexpr explicit operator bool () const {
357+ return kind != Kind::None;
358+ }
359+ };
360+
361+ constexpr inline bool is_alias_placeholder_name (std::string_view member_name) {
362+ return member_name.starts_with (" __deco_alias_wrapper" ) ||
363+ (!member_name.empty () &&
364+ std::all_of (member_name.begin (), member_name.end (), [](char ch) { return ch == ' _' ; }));
365+ }
366+
303367struct DecoFields {
304368 // if true, it's required if its category occurs in options.
305369 bool required = true ;
@@ -309,9 +373,37 @@ struct DecoFields {
309373 constexpr DecoFields () = default;
310374};
311375
376+ struct MetaVarField {
377+ std::string_view value = " <value>" ;
378+ bool explicit_value = false ;
379+
380+ constexpr MetaVarField () = default;
381+
382+ constexpr MetaVarField (std::string_view value, bool explicit_value = false ) :
383+ value(value), explicit_value(explicit_value) {}
384+
385+ constexpr auto operator =(std::string_view new_value) -> MetaVarField& {
386+ value = new_value;
387+ explicit_value = true ;
388+ return *this ;
389+ }
390+
391+ constexpr auto empty () const -> bool {
392+ return value.empty ();
393+ }
394+
395+ constexpr auto is_explicit () const -> bool {
396+ return explicit_value;
397+ }
398+
399+ constexpr operator std::string_view () const {
400+ return value;
401+ }
402+ };
403+
312404struct CommonOptionFields : DecoFields {
313405 std::string_view help = " not provided" ;
314- std::string_view meta_var = " <value> " ;
406+ MetaVarField meta_var{} ;
315407
316408 constexpr CommonOptionFields () = default;
317409};
@@ -371,7 +463,7 @@ struct ConfigFields {
371463 ConfigOverrideField<bool > required = true ;
372464 ConfigOverrideField<CategoryRef> category = default_category;
373465 ConfigOverrideField<std::string_view> help = " not provided" ;
374- ConfigOverrideField<std::string_view > meta_var = " <value>" ;
466+ ConfigOverrideField<MetaVarField > meta_var = MetaVarField{ " <value>" , false } ;
375467
376468 enum class Type : char {
377469 Start = 0 ,
@@ -420,6 +512,33 @@ struct MultiFields : NamedOptionFields {
420512 constexpr MultiFields () = default;
421513};
422514
515+ struct AliasFields : NamedOptionFields {
516+ AliasForwardField forward;
517+ constexpr AliasFields () = default;
518+ };
519+
520+ struct FlagAliasFields : AliasFields {
521+ constexpr static DecoType deco_field_ty = DecoType::Flag;
522+ constexpr FlagAliasFields () = default;
523+ };
524+
525+ struct KVAliasFields : AliasFields {
526+ constexpr static DecoType deco_field_ty = DecoType::KV;
527+ char style = KVStyle::Separate;
528+ constexpr KVAliasFields () = default;
529+ };
530+
531+ struct CommaJoinedAliasFields : AliasFields {
532+ constexpr static DecoType deco_field_ty = DecoType::CommaJoined;
533+ constexpr CommaJoinedAliasFields () = default;
534+ };
535+
536+ struct MultiAliasFields : AliasFields {
537+ constexpr static DecoType deco_field_ty = DecoType::Multi;
538+ unsigned arg_num = 1 ;
539+ constexpr MultiAliasFields () = default;
540+ };
541+
423542struct DecoOptionBase {
424543 constexpr virtual ~DecoOptionBase () = default ;
425544 // return error message if parsing fails, otherwise return std::nullopt
@@ -525,6 +644,27 @@ inline bool iequals_ascii(std::string_view lhs, std::string_view rhs) {
525644 return true ;
526645}
527646
647+ template <typename EnumTy>
648+ std::string format_invalid_enum_value (std::string_view text) {
649+ std::string message = " invalid enum value: " ;
650+ message += text;
651+
652+ const auto & values = eventide::serde::spelling::enum_strings<EnumTy>();
653+ if (values.empty ()) {
654+ return message;
655+ }
656+
657+ message += " (supported: " ;
658+ for (std::size_t i = 0 ; i < values.size (); ++i) {
659+ if (i != 0 ) {
660+ message += " , " ;
661+ }
662+ message += values[i];
663+ }
664+ message += " )" ;
665+ return message;
666+ }
667+
528668template <typename ResTy>
529669std::optional<std::string> parse_primitive_scalar (ResTy& out, std::string_view text) {
530670 if constexpr (std::same_as<ResTy, bool >) {
@@ -551,6 +691,12 @@ std::optional<std::string> parse_primitive_scalar(ResTy& out, std::string_view t
551691 return std::nullopt ;
552692 } else if constexpr (std::same_as<ResTy, long double >) {
553693 return " unsupported floating-point type: long double" ;
694+ } else if constexpr (std::is_enum_v<ResTy>) {
695+ if (auto parsed = eventide::serde::spelling::map_string_to_enum<ResTy>(text)) {
696+ out = *parsed;
697+ return std::nullopt ;
698+ }
699+ return format_invalid_enum_value<ResTy>(text);
554700 } else if constexpr (std::floating_point<ResTy>) {
555701 std::string copy (text);
556702 char * parse_end = nullptr ;
0 commit comments