1 /++
2 This implements common de/serialization routines.
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
6 Authors: Ilya Yaroshenko
7 
8 Macros:
9 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
10 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
11 +/
12 module mir.serde;
13 
14 import mir.functional: naryFun;
15 import mir.reflection;
16 import std.traits: TemplateArgsOf, EnumMembers, hasUDA, isAggregateType;
17 
18 version (D_Exceptions)
19 {
20     /++
21     Serde Exception
22     +/
23     class SerdeException : Exception
24     {
25         ///
26         this(
27             string msg,
28             string file = __FILE__,
29             size_t line = __LINE__,
30             Throwable next = null) pure nothrow @nogc @safe 
31         {
32             super(msg, file, line, next);
33         }
34 
35         ///
36         this(
37             string msg,
38             Throwable next,
39             string file = __FILE__,
40             size_t line = __LINE__,
41             ) pure nothrow @nogc @safe 
42         {
43             this(msg, file, line, next);
44         }
45 
46         SerdeException toMutable() @trusted pure nothrow @nogc const
47         {
48             return cast() this;
49         }
50 
51         alias toMutable this;
52     }
53 
54     /++
55     Serde Exception with formatting support
56     +/
57     class SerdeMirException : SerdeException
58     {
59         import mir.exception: MirThrowableImpl, mirExceptionInitilizePayloadImpl;
60 
61         enum maxMsgLen = 447;
62 
63         ///
64         mixin MirThrowableImpl;
65     }
66 }
67 
68 /++
69 Constructs annotated type.
70 +/
71 template SerdeAnnotated(T, string annotation)
72 {
73     ///
74     @serdeAlgebraicAnnotation(annotation)
75     @serdeProxy!T
76     struct SerdeAnnotated
77     {
78         ///
79         T value;
80         ///
81         alias value this;
82     }
83 }
84 
85 /++
86 Helper enumeration for for serializer .
87 Use negative `int` values for user defined targets.
88 +/
89 enum SerdeTarget : int
90 {
91     ///
92     ion,
93     ///
94     json,
95     ///
96     cbor,
97     ///
98     msgpack,
99     ///
100     yaml,
101     ///
102     csv,
103     ///
104     excel,
105     ///
106     bloomberg,
107     ///
108     typedJson,
109 }
110 
111 /++
112 Attribute for key overloading during Serialization and Deserialization.
113 The first argument overloads the key value during serialization unless `serdeKeyOut` is given.
114 +/
115 struct serdeKeys
116 {
117     ///
118     immutable(string)[] keys;
119 
120 @trusted pure nothrow @nogc:
121     ///
122     this(immutable(string)[] keys...) { this.keys = keys; }
123 }
124 
125 /++
126 Attribute for key overloading during serialization.
127 +/
128 struct serdeKeyOut
129 {
130     ///
131     string key;
132 
133 @safe pure nothrow @nogc:
134     ///
135     this(string key) { this.key = key; }
136 }
137 
138 /++
139 The attribute should be used as a hind for scripting languages to register type deserializer in the type system.
140 
141 The attribute should be applied to a type definition.
142 +/
143 enum serdeRegister;
144 
145 /++
146 The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute.
147 
148 This feature is used in $(MIR_PACKAGE mir-ion).
149 +/
150 enum serdeAnnotation;
151 
152 
153 private template serdeIsAnnotationMemberIn(T)
154 {
155     enum bool serdeIsAnnotationMemberIn(string member)
156           = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
157         && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
158         && !hasUDA!(__traits(getMember, T, member), serdeIgnoreIn);
159 }
160 
161 /++
162 +/
163 template serdeGetAnnotationMembersIn(T)
164 {
165     import std.meta: aliasSeqOf, Filter;
166     static if (isAggregateType!T)
167         enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(DeserializableMembers!T))];
168     else
169         enum string[] serdeGetAnnotationMembersIn = null;
170 }
171 
172 
173 ///
174 version(mir_test) unittest
175 {
176     struct S
177     {
178         double data;
179 
180         @serdeAnnotation
181         string a;
182         @serdeAnnotation @serdeIgnoreIn
183         string b;
184         @serdeAnnotation @serdeIgnoreOut
185         string c;
186         @serdeAnnotation @serdeIgnore
187         string d;
188     }
189 
190     static assert(serdeGetAnnotationMembersIn!int == []);
191     static assert(serdeGetAnnotationMembersIn!S == ["a", "c"]);
192 }
193 
194 private template serdeIsAnnotationMemberOut(T)
195 {
196     enum bool serdeIsAnnotationMemberOut(string member)
197           = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
198         && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
199         && !hasUDA!(__traits(getMember, T, member), serdeIgnoreOut);
200 }
201 
202 /++
203 +/
204 template serdeGetAnnotationMembersOut(T)
205 {
206     import std.meta: aliasSeqOf, Filter;
207     static if (isAggregateType!T)
208         enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(DeserializableMembers!T))];
209     else
210         enum string[] serdeGetAnnotationMembersOut = null;
211 }
212 
213 ///
214 version(mir_test) unittest
215 {
216     struct S
217     {
218         double data;
219 
220         @serdeAnnotation
221         string a;
222         @serdeAnnotation @serdeIgnoreIn
223         string b;
224         @serdeAnnotation @serdeIgnoreOut
225         string c;
226         @serdeAnnotation @serdeIgnore
227         string d;
228     }
229 
230     static assert(serdeGetAnnotationMembersOut!int == []);
231     static assert(serdeGetAnnotationMembersOut!S == ["a", "b"]);
232 }
233 
234 /++
235 An annotation / attribute for algebraic types deserialization.
236 
237 This feature is used in $(MIR_PACKAGE mir-ion) for $(GMREF mir-core, mir,algebraic).
238 +/
239 struct serdeAlgebraicAnnotation
240 {
241     ///
242     string annotation;
243 
244 @safe pure nothrow @nogc:
245     ///
246     this(string annotation) { this.annotation = annotation; }
247 }
248 
249 /++
250 +/
251 template serdeHasAlgebraicAnnotation(T)
252 {
253     static if (isAggregateType!T || is(T == enum))
254     {
255         static if (hasUDA!(T, serdeAlgebraicAnnotation))
256         {
257             enum serdeHasAlgebraicAnnotation = true;
258         }
259         else
260         static if (__traits(getAliasThis, T).length)
261         {
262             T* aggregate;
263             alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
264             enum serdeHasAlgebraicAnnotation = .serdeHasAlgebraicAnnotation!A;
265         }
266         else
267         {
268             enum serdeHasAlgebraicAnnotation = false;
269         }
270     }
271     else
272     {
273         enum serdeHasAlgebraicAnnotation = false;
274     }
275 }
276 
277 /++
278 +/
279 template serdeGetAlgebraicAnnotation(T)
280 {
281     static if (hasUDA!(T, serdeAlgebraicAnnotation))
282     {
283         enum string serdeGetAlgebraicAnnotation = getUDA!(T, serdeAlgebraicAnnotation).annotation;
284     }
285     else
286     {
287         T* aggregate;
288         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
289         enum serdeGetAlgebraicAnnotation = .serdeGetAlgebraicAnnotation!A;
290     }
291 }
292 
293 /++
294 Returns:
295     immutable array of the input keys for the symbol or enum value
296 +/
297 template serdeGetKeysIn(alias symbol)
298 {
299     static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
300         enum immutable(string)[] serdeGetKeysIn = null;
301     else
302     static if (hasUDA!(symbol, serdeKeys))
303         enum immutable(string)[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys;
304     else
305         enum immutable(string)[] serdeGetKeysIn = [__traits(identifier, symbol)];
306 }
307 
308 /// ditto
309 immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
310     if (is(T == enum))
311 {
312     foreach (i, member; EnumMembers!T)
313     {{
314         alias all = __traits(getAttributes, EnumMembers!T[i]);
315     }}
316 
317     import std.meta: staticMap;
318     static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)];
319     static if (__VERSION__ < 2093)
320     {
321         final switch (value)
322         {
323             foreach (i, member; EnumMembers!T)
324             {
325                 case member:
326                     return ret[i];
327             }
328         }
329     }
330     else
331     {
332     import mir.enums: getEnumIndex;
333     uint index = void;
334     if (getEnumIndex(value, index))
335         return ret[index];
336     assert(0);
337     }
338 }
339 
340 ///
341 version(mir_test) unittest
342 {
343     struct S
344     {
345         int f;
346 
347         @serdeKeys("D", "t")
348         int d;
349 
350         @serdeIgnore
351         int i;
352 
353         @serdeIgnoreIn
354         int ii;
355 
356         @serdeIgnoreOut
357         int io;
358 
359         void p(int) @property {}
360     }
361 
362     static assert(serdeGetKeysIn!(S.f) == ["f"]);
363     static assert(serdeGetKeysIn!(S.d) == ["D", "t"]);
364     static assert(serdeGetKeysIn!(S.i) == null);
365     static assert(serdeGetKeysIn!(S.ii) == null);
366     static assert(serdeGetKeysIn!(S.io) == ["io"]);
367     static assert(serdeGetKeysIn!(S.p) == ["p"]);
368 }
369 
370 ///
371 version(mir_test) unittest
372 {
373     enum E
374     {
375         @serdeKeys("A", "alpha")
376         a,
377         @serdeKeys("B", "beta")
378         b,
379         c,
380     }
381 
382     static assert (serdeGetKeysIn(E.a) == ["A", "alpha"], serdeGetKeysIn(E.a));
383     static assert (serdeGetKeysIn(E.c) == ["c"]);
384 }
385 
386 /++
387 Returns:
388     output key for the symbol or enum value
389 +/
390 template serdeGetKeyOut(alias symbol)
391 {
392     static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut))
393         enum string serdeGetKeyOut = null;
394     else
395     static if (hasUDA!(symbol, serdeKeyOut))
396         enum string serdeGetKeyOut = getUDA!(symbol, serdeKeyOut).key;
397     else
398     static if (hasUDA!(symbol, serdeKeys))
399         enum string serdeGetKeyOut = getUDA!(symbol, serdeKeys).keys[0];
400     else
401         enum string serdeGetKeyOut = __traits(identifier, symbol);
402 }
403 
404 ///ditto
405 @safe pure nothrow @nogc
406 string serdeGetKeyOut(T)(const T value)
407     if (is(T == enum))
408 {
409     foreach (i, member; EnumMembers!T)
410     {{
411         alias all = __traits(getAttributes, EnumMembers!T[i]);
412     }}
413 
414     static if (__VERSION__ < 2093)
415     {
416         import std.meta: staticMap;
417         static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
418         final switch (value)
419         {
420             foreach (i, member; EnumMembers!T)
421             {
422                 case member:
423                     return ret[i];
424             }
425         }
426     }
427     else
428     {
429     import std.meta: staticMap;
430     import mir.enums: getEnumIndex;
431     static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
432     uint index = void;
433     if (getEnumIndex(value, index))
434         return ret[index];
435     assert(0);
436     }
437 }
438 
439 ///
440 version(mir_test) unittest
441 {
442     struct S
443     {
444         int f;
445 
446         @serdeKeys("D", "t")
447         int d;
448 
449         @serdeIgnore
450         int i;
451 
452         @serdeIgnoreIn
453         int ii;
454 
455         @serdeIgnoreOut
456         int io;
457 
458         @serdeKeys("P")
459         @serdeKeyOut("")
460         void p(int) @property {}
461     }
462 
463     static assert(serdeGetKeyOut!(S.f) == "f");
464     static assert(serdeGetKeyOut!(S.d) == "D");
465     static assert(serdeGetKeyOut!(S.i) is null);
466     static assert(serdeGetKeyOut!(S.ii) == "ii");
467     static assert(serdeGetKeyOut!(S.io) is null);
468     static assert(serdeGetKeyOut!(S.p) !is null);
469     static assert(serdeGetKeyOut!(S.p) == "");
470 }
471 
472 ///
473 version(mir_test) unittest
474 {
475     enum E
476     {
477         @serdeKeys("A", "alpha")
478         a,
479         @serdeKeys("B", "beta")
480         @serdeKeyOut("o")
481         b,
482         c,
483     }
484 
485     static assert (serdeGetKeyOut(E.a) == "A");
486     static assert (serdeGetKeyOut(E.b) == "o");
487     static assert (serdeGetKeyOut(E.c) == "c");
488 }
489 
490 /++
491 Attribute to ignore field.
492 
493 See_also: $(LREF serdeIgnoreIn) $(LREF serdeIgnoreOut)
494 +/
495 enum serdeIgnore;
496 
497 /++
498 Attribute to ignore field during deserialization.
499 
500 See_also: $(LREF serdeIgnoreInIfAggregate)
501 +/
502 enum serdeIgnoreIn;
503 
504 /++
505 Attribute to ignore field during serialization.
506 +/
507 enum serdeIgnoreOut;
508 
509 /++
510 Attribute to ignore a field during deserialization when equals to its default value.
511 Do not use it on void initialized fields or aggregates with void initialized fields, recursively.
512 +/
513 enum serdeIgnoreDefault;
514 
515 ///
516 version(mir_test) unittest
517 {
518     struct S
519     {
520         @serdeIgnoreDefault
521         double d = 0; // skips field if 0 during deserialization
522     }
523 
524     import std.traits: hasUDA;
525 
526     static assert(hasUDA!(S.d, serdeIgnoreDefault));
527 }
528 
529 /++
530 +/
531 
532 /++
533 Serialization proxy.
534 +/
535 struct serdeProxy(T);
536 
537 ///
538 version(mir_test) unittest
539 {
540     import mir.small_string;
541 
542     struct S
543     {
544         @serdeProxy!(SmallString!32)
545         double d;
546     }
547 
548     import std.traits: hasUDA;
549 
550     static assert(hasUDA!(S.d, serdeProxy));
551     static assert(hasUDA!(S.d, serdeProxy!(SmallString!32)));
552     static assert(is(serdeGetProxy!(S.d) == SmallString!32));
553 }
554 
555 /++
556 +/
557 alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0];
558 
559 /++
560 Can be applied only to fields that can be constructed from strings.
561 Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
562 Use this attributes only for strings that would not be used after the input data deallocation.
563 +/
564 deprecated("use @serdeScoped @serdeProxy!(const(char)[]) instead") enum serdeScopeStringProxy;
565 
566 /++
567 Attributes to conditional ignore field during serialization.
568 
569 The predicate should be aplied to the member, to the aggregate type.
570 
571 See_also: $(LREF serdeIgnoreOutIfAggregate)
572 +/
573 struct serdeIgnoreOutIf(alias pred);
574 
575 /++
576 +/
577 alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]);
578 
579 /++
580 Attributes to conditional ignore field during serialization.
581 
582 The predicate should be aplied to the aggregate value, not to the member.
583 
584 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIf), $(LREF serdeIgnoreInIfAggregate)
585 +/
586 struct serdeIgnoreOutIfAggregate(alias pred);
587 
588 /++
589 +/
590 alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIfAggregate))[0]);
591 
592 /++
593 Attributes to conditional ignore field during deserialization.
594 
595 The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate.
596 
597 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreIn)
598 +/
599 struct serdeIgnoreInIfAggregate(alias pred);
600 
601 /++
602 +/
603 alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreInIfAggregate))[0]);
604 
605 /++
606 Attributes to conditional ignore field during serialization and deserialization.
607 
608 The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate.
609 
610 The predicate should be aplied to the aggregate value, not to the member.
611 
612 See_also: $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreInIfAggregate) $ $(LREF serdeIgnore)
613 +/
614 struct serdeIgnoreIfAggregate(alias pred);
615 
616 /++
617 +/
618 alias serdeGetIgnoreIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreIfAggregate))[0]);
619 
620 /++
621 Allows to use flexible deserialization rules such as conversion from input string to numeric types.
622 +/
623 enum serdeFlexible;
624 
625 /++
626 Allows serialize / deserialize fields like arrays.
627 
628 A range or a container should be iterable for serialization.
629 Following code should compile:
630 ------
631 foreach(ref value; yourRangeOrContainer)
632 {
633     ...
634 }
635 ------
636 
637 `put(value)` method is used for deserialization.
638 
639 See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
640 +/
641 enum serdeLikeList;
642 
643 /++
644 Allows serialize / deserialize fields like objects.
645 
646 Object should have `opApply` method to allow serialization.
647 Following code should compile:
648 ------
649 foreach(key, value; yourObject)
650 {
651     ...
652 }
653 ------
654 Object should have only one `opApply` method with 2 argument to allow automatic value type deduction.
655 
656 `opIndexAssign` or `opIndex` is used for deserialization to support required syntax:
657 -----
658 yourObject["key"] = value;
659 -----
660 Multiple value types is supported for deserialization.
661 
662 See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
663 +/
664 enum serdeLikeStruct;
665 
666 /++
667 Ignore keys for object and enum members.
668 Should be applied to members or enum type itself.
669 +/
670 enum serdeIgnoreCase;
671 
672 ///
673 bool hasSerdeIgnoreCase(T)(T value)
674     if (is(T == enum))
675 {
676     static if (hasUDA!(T, serdeIgnoreCase))
677     {
678         return true;
679     }
680     else
681     {
682         foreach (i, member; EnumMembers!T)
683         {
684             alias all = __traits(getAttributes, EnumMembers!T[i]);
685             if (value == member)
686                 return hasUDA!(EnumMembers!T[i], serdeIgnoreCase);
687         }
688         assert(0);
689     }
690 }
691 
692 ///
693 version(mir_test) unittest
694 {
695     enum E
696     {
697         @serdeIgnoreCase
698         a,
699         b,
700         @serdeIgnoreCase
701         c,
702         d,
703     }
704 
705     static assert(hasSerdeIgnoreCase(E.a));
706     static assert(!hasSerdeIgnoreCase(E.b));
707     static assert(hasSerdeIgnoreCase(E.c));
708     static assert(!hasSerdeIgnoreCase(E.d));
709 }
710 
711 ///
712 version(mir_test) unittest
713 {
714     @serdeIgnoreCase
715     enum E
716     {
717         a,
718         b,
719         c,
720         d,
721     }
722 
723     static assert(hasSerdeIgnoreCase(E.a));
724     static assert(hasSerdeIgnoreCase(E.b));
725     static assert(hasSerdeIgnoreCase(E.c));
726     static assert(hasSerdeIgnoreCase(E.d));
727 }
728 
729 /++
730 Can be applied only to strings fields.
731 Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
732 Use this attributes only for strings or arrays that would not be used after deallocation.
733 +/
734 enum serdeScoped;
735 
736 /++
737 Attribute that force deserializer to throw an exception that the field hasn't been not found in the input.
738 +/
739 enum serdeRequired;
740 
741 /++
742 Attribute that allow deserializer to do not throw an exception if the field hasn't been not found in the input.
743 +/
744 enum serdeOptional;
745 
746 /++
747 Attribute that allow deserializer to don't throw an exception that the field matches multiple keys in the object.
748 +/
749 enum serdeAllowMultiple;
750 
751 /++
752 Attributes for in transformation.
753 Return type of in transformation must be implicitly convertable to the type of the field.
754 In transformation would be applied after serialization proxy if any.
755 
756 +/
757 struct serdeTransformIn(alias fun) {}
758 
759 /++
760 Returns: unary function of underlaying alias of $(LREF serdeTransformIn)
761 +/
762 alias serdeGetTransformIn(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]);
763 
764 /++
765 Attributes for out transformation.
766 Return type of out transformation may be differ from the type of the field.
767 Out transformation would be applied before serialization proxy if any.
768 +/
769 struct serdeTransformOut(alias fun) {}
770 
771 /++
772 Returns: unary function of underlaying alias of $(LREF serdeTransformOut)
773 +/
774 alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);
775 
776 /++
777 +/
778 bool serdeParseEnum(E)(const char[] str, ref E res)
779     @safe pure nothrow @nogc
780 {
781     static if (__VERSION__ < 2093)
782     {
783         static if (hasUDA!(E, serdeIgnoreCase))
784         {
785             import mir.format: stringBuf;
786             stringBuf buf;
787             buf << str;
788             auto ustr = buf.data.fastToUpperInPlace;
789         }
790         else
791         {
792             alias ustr = str;
793         }
794         switch(ustr)
795         {
796             foreach(i, member; EnumMembers!E)
797             {{
798                 enum initKeys = serdeGetKeysIn(EnumMembers!E[i]);
799                 static if (hasUDA!(E, serdeIgnoreCase))
800                 {
801                     import mir.ndslice.topology: map;
802                     import mir.array.allocation: array;
803                     enum keys = initKeys.map!fastLazyToUpper.map!array.array;
804                 }
805                 else
806                 {
807                     enum keys = initKeys;
808                 }
809                 static assert (keys.length, "At least one input enum key is required");
810                 static foreach (key; keys)
811                 {
812                     case key:
813                 }
814                 res = member;
815                 return true;
816             }}
817             default:
818                 return false;
819         }
820     }
821     else
822     {
823     import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex;
824     import mir.utility: _expect;
825 
826     uint index = void;
827     if (getEnumIndexFromKey!(E, hasUDA!(E, serdeIgnoreCase), serdeGetKeysIn)(str, index)._expect(true))
828     {
829         res = unsafeEnumFromIndex!E(index);
830         return true;
831     }
832     return false;
833     }
834 }
835 
836 ///
837 version(mir_test) unittest
838 {
839     enum E
840     {
841         @serdeKeys("A", "alpha")
842         a,
843         @serdeKeys("B", "beta")
844         b,
845         c,
846     }
847 
848     auto e = E.c;
849     assert(serdeParseEnum("A", e));
850     assert(e == E.a);
851     assert(serdeParseEnum("alpha", e));
852     assert(e == E.a);
853     assert(serdeParseEnum("beta", e));
854     assert(e == E.b);
855     assert(serdeParseEnum("B", e));
856     assert(e == E.b);
857     assert(serdeParseEnum("c", e));
858     assert(e == E.c);
859 
860     assert(!serdeParseEnum("C", e));
861     assert(!serdeParseEnum("Alpha", e));
862 }
863 
864 /// Case insensitive
865 version(mir_test) unittest
866 {
867     @serdeIgnoreCase // supported for the whole type
868     enum E
869     {
870         @serdeKeys("A", "alpha")
871         a,
872         @serdeKeys("B", "beta")
873         b,
874         c,
875     }
876 
877     auto e = E.c;
878     assert(serdeParseEnum("a", e));
879     assert(e == E.a);
880     assert(serdeParseEnum("alpha", e));
881     assert(e == E.a);
882     assert(serdeParseEnum("BETA", e));
883     assert(e == E.b);
884     assert(serdeParseEnum("b", e));
885     assert(e == E.b);
886     assert(serdeParseEnum("C", e));
887     assert(e == E.c);
888 }
889 
890 /++
891 Deserialization member type
892 +/
893 template serdeDeserializationMemberType(T, string member)
894 {
895     import std.traits: Unqual, Parameters;
896     T* aggregate;
897     static if (hasField!(T, member))
898     {
899         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member));
900     }
901     else
902     static if (__traits(compiles, &__traits(getMember, *aggregate, member)()) || __traits(getOverloads, *aggregate, member).length > 1)
903     {
904         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)());
905     }
906     else
907     {
908         alias serdeDeserializationMemberType = Unqual!(Parameters!(__traits(getMember, *aggregate, member))[0]);
909     }
910 }
911 
912 /// ditto
913 template serdeDeserializationMemberType(T)
914 {
915     ///
916     alias serdeDeserializationMemberType(string member) = .serdeDeserializationMemberType!(T, member);
917 }
918 
919 
920 /++
921 Is deserializable member
922 +/
923 template serdeIsDeserializable(T)
924 {
925     ///
926     enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(__traits(getMember, T, member)).length > 0;
927 }
928 
929 ///
930 version(mir_test) unittest
931 {
932 
933     static struct S
934     {
935         @serdeIgnore
936         int i;
937 
938         @serdeKeys("a", "b")
939         int a;
940     }
941 
942     alias serdeIsDeserializableInS = serdeIsDeserializable!S;
943     static assert (!serdeIsDeserializableInS!"i");
944     static assert (serdeIsDeserializableInS!"a");
945 }
946 
947 /++
948 Serialization member type
949 +/
950 template serdeSerializationMemberType(T, string member)
951 {
952     import std.traits: Unqual, Parameters;
953     T* aggregate;
954     static if (hasField!(T, member))
955     {
956         alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member));
957     }
958     else
959     {
960         alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)());
961     }
962 }
963 
964 /// ditto
965 template serdeSerializationMemberType(T)
966 {
967     ///
968     alias serdeSerializationMemberType(string member) = .serdeSerializationMemberType!(T, member);
969 }
970 
971 
972 /++
973 Is deserializable member
974 +/
975 template serdeIsSerializable(T)
976 {
977     ///
978     enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) !is null;
979 }
980 
981 ///
982 version(mir_test) unittest
983 {
984 
985     static struct S
986     {
987         @serdeIgnore
988         int i;
989 
990         @serdeKeys("a", "b")
991         int a;
992     }
993 
994     alias serdeIsSerializableInS = serdeIsSerializable!S;
995     static assert (!serdeIsSerializableInS!"i");
996     static assert (serdeIsSerializableInS!"a");
997 }
998 
999 /++
1000 Final proxy type
1001 +/
1002 template serdeGetFinalProxy(T)
1003 {
1004     import mir.timestamp: Timestamp;
1005     import std.traits: hasUDA, isAggregateType;
1006     static if (isAggregateType!T || is(T == enum))
1007     {
1008         static if (hasUDA!(T, serdeProxy))
1009         {
1010             alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T);
1011         }
1012         else
1013         static if (isAggregateType!T && is(typeof(Timestamp(T.init))))
1014         {
1015             alias serdeGetFinalProxy = string;
1016         }
1017         else
1018         {
1019             alias serdeGetFinalProxy = T;
1020         }
1021     }
1022     else
1023     {
1024         alias serdeGetFinalProxy = T;
1025     }
1026 }
1027 
1028 ///
1029 version(mir_test) unittest
1030 {
1031 
1032     @serdeProxy!string
1033     static struct A {}
1034 
1035     @serdeProxy!A
1036     static struct B {}
1037 
1038     @serdeProxy!B
1039     static struct C {}
1040 
1041     static assert (is(serdeGetFinalProxy!C == string), serdeGetFinalProxy!C.stringof);
1042     static assert (is(serdeGetFinalProxy!string == string));
1043 }
1044 
1045 /++
1046 Final deep proxy type
1047 +/
1048 template serdeGetFinalDeepProxy(T)
1049 {
1050     import mir.timestamp: Timestamp;
1051     import std.traits: Unqual, hasUDA, isAggregateType, isArray, ForeachType;
1052     static if (isAggregateType!T || is(T == enum))
1053     {
1054         static if (hasUDA!(T, serdeProxy))
1055         {
1056             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T);
1057         }
1058         else
1059         static if (isAggregateType!T && is(typeof(Timestamp(T.init))))
1060         {
1061             alias serdeGetFinalDeepProxy = string;
1062         }
1063         else
1064         static if (__traits(hasMember, T, "serdeKeysProxy"))
1065         {
1066             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy);
1067         }
1068         else
1069         // static if (is(T == enum))
1070         // {
1071         //     alias serdeGetFinalDeepProxy = typeof(null);
1072         // }
1073         // else
1074         {
1075             alias serdeGetFinalDeepProxy = T;
1076         }
1077     }
1078     else
1079     static if (isArray!T)
1080     {
1081         alias E = Unqual!(ForeachType!T);
1082         static if (isAggregateType!E || is(E == enum))
1083             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
1084         else
1085             alias serdeGetFinalDeepProxy = T;
1086     }
1087     else
1088     static if (is(immutable T == immutable V[K], K, V))
1089     {
1090         alias E = serdeGetFinalDeepProxy!(Unqual!V);
1091         static if (isAggregateType!E || is(E == enum))
1092             alias serdeGetFinalDeepProxy = E;
1093         else
1094             alias serdeGetFinalDeepProxy = T;
1095     }
1096     else
1097     {
1098         alias serdeGetFinalDeepProxy = T;
1099     }
1100 }
1101 
1102 ///
1103 version(mir_test) unittest
1104 {
1105 
1106     @serdeProxy!string
1107     static struct A {}
1108 
1109     enum E {a,b,c}
1110 
1111     @serdeProxy!(A[E])
1112     static struct B {}
1113 
1114     @serdeProxy!(B[])
1115     static struct C {}
1116 
1117     static assert (is(serdeGetFinalDeepProxy!C == A[E]));
1118     static assert (is(serdeGetFinalDeepProxy!string == string));
1119 }
1120 
1121 /++
1122 Final proxy type deserializable members
1123 +/
1124 template serdeFinalProxyDeserializableMembers(T)
1125 {
1126     import std.meta: Filter, aliasSeqOf;
1127     alias P = serdeGetFinalProxy!T;
1128     static if (isAggregateType!P || is(P == enum))
1129         enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
1130     else
1131     // static if (is(P == enum))
1132     //     enum string[] serdeFinalProxyDeserializableMembers = serdeGetKeysIn!P;
1133     // else
1134         enum string[] serdeFinalProxyDeserializableMembers = null;
1135 }
1136 
1137 ///
1138 version(mir_test) unittest
1139 {
1140 
1141     static struct A
1142     {
1143         @serdeIgnore
1144         int i;
1145 
1146         @serdeKeys("a", "b")
1147         int m;
1148     }
1149 
1150     @serdeProxy!A
1151     static struct B {}
1152 
1153     @serdeProxy!B
1154     static struct C {}
1155 
1156     static assert (serdeFinalProxyDeserializableMembers!C == ["m"]);
1157 }
1158 
1159 /++
1160 Final deep proxy type serializable members
1161 +/
1162 template serdeFinalDeepProxySerializableMembers(T)
1163 {
1164     import std.traits: isAggregateType;
1165     import std.meta: Filter, aliasSeqOf;
1166     alias P = serdeGetFinalDeepProxy!T;
1167     static if (isAggregateType!P || is(P == enum))
1168         enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
1169     else
1170     // static if (is(P == enum))
1171     //     enum string[] serdeFinalDeepProxySerializableMembers = [serdeGetKeyOut!P];
1172     // else
1173         enum string[] serdeFinalDeepProxySerializableMembers = null;
1174 }
1175 
1176 ///
1177 version(mir_test) unittest
1178 {
1179 
1180     static struct A
1181     {
1182         @serdeIgnore
1183         int i;
1184 
1185         @serdeKeys("a", "b")
1186         int m;
1187     }
1188 
1189     @serdeProxy!(A[string])
1190     static struct B {}
1191 
1192     @serdeProxy!(B[])
1193     static struct C {}
1194 
1195     static assert (serdeFinalDeepProxySerializableMembers!C == ["m"]);
1196 }
1197 
1198 /++
1199 Final proxy type deserializable members
1200 +/
1201 template serdeFinalProxySerializableMembers(T)
1202 {
1203     import std.meta: Filter, aliasSeqOf;
1204     alias P = serdeGetFinalProxy!T;
1205     static if (isAggregateType!P || is(P == enum))
1206         enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
1207     else
1208     // static if (is(P == enum))
1209     //     enum string[] serdeFinalProxySerializableMembers = [serdeGetKeyOut!P];
1210     // else
1211         enum string[] serdeFinalProxySerializableMembers = null;
1212 }
1213 
1214 ///
1215 version(mir_test) unittest
1216 {
1217 
1218     static struct A
1219     {
1220         @serdeIgnore
1221         int i;
1222 
1223         @serdeKeys("a", "b")
1224         int m;
1225     }
1226 
1227     @serdeProxy!A
1228     static struct B {}
1229 
1230     @serdeProxy!B
1231     static struct C {}
1232 
1233     static assert (serdeFinalProxySerializableMembers!C == ["m"]);
1234 }
1235 
1236 /++
1237 Final deep proxy type serializable members
1238 +/
1239 template serdeFinalDeepProxyDeserializableMembers(T)
1240 {
1241     import std.traits: isAggregateType;
1242     import std.meta: Filter, aliasSeqOf;
1243     alias P = serdeGetFinalDeepProxy!T;
1244     static if (isAggregateType!P || is(P == enum))
1245         enum string[] serdeFinalDeepProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
1246     else
1247     // static if (is(P == enum))
1248     //     enum string[] serdeFinalDeepProxyDeserializableMembers = serdeGetKeysIn!P;
1249     // else
1250         enum string[] serdeFinalDeepProxyDeserializableMembers = null;
1251 }
1252 
1253 ///
1254 version(mir_test) unittest
1255 {
1256     static struct A
1257     {
1258         @serdeIgnore
1259         int i;
1260 
1261         @serdeKeys("a", "b")
1262         int m;
1263     }
1264 
1265     @serdeProxy!(A[string])
1266     static struct B {}
1267 
1268     @serdeProxy!(B[])
1269     static struct C {}
1270 
1271     static assert (serdeFinalDeepProxyDeserializableMembers!C == ["m"]);
1272 }
1273 
1274 /++
1275 Deserialization member final proxy type
1276 +/
1277 template serdeFinalDeserializationMemberType(T, string member)
1278 {
1279     import std.traits: hasUDA;
1280     static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
1281     {
1282         alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
1283     }
1284     else
1285     {
1286         alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeDeserializationMemberType!(T, member));
1287     }
1288 }
1289 
1290 /// ditto
1291 template serdeFinalDeserializationMemberType(T)
1292 {
1293     ///
1294     alias serdeFinalDeserializationMemberType(string member) = .serdeFinalDeserializationMemberType!(T, member);
1295 }
1296 
1297 ///
1298 version(mir_test) unittest
1299 {
1300 
1301     static struct A
1302     {
1303         
1304     }
1305 
1306     @serdeProxy!A
1307     static struct B {}
1308 
1309     @serdeProxy!B
1310     static struct C {}
1311 
1312 
1313     @serdeProxy!double
1314     struct E {}
1315 
1316     struct D
1317     {
1318         C c;
1319 
1320         @serdeProxy!E
1321         int d;
1322     }
1323 
1324     static assert (is(serdeFinalDeserializationMemberType!(D, "c") == A));
1325     static assert (is(serdeFinalDeserializationMemberType!(D, "d") == double));
1326 }
1327 
1328 /++
1329 Deserialization members final proxy types
1330 +/
1331 template serdeDeserializationFinalProxyMemberTypes(T)
1332 {
1333     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
1334     alias serdeDeserializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalProxyDeserializableMembers!T))));
1335 }
1336 
1337 ///
1338 version(mir_test) unittest
1339 {
1340 
1341     static struct A {}
1342 
1343     @serdeProxy!A
1344     static struct B {}
1345 
1346     @serdeProxy!B
1347     static struct C {}
1348 
1349     @serdeProxy!B
1350     static struct E {}
1351 
1352     static struct D
1353     {
1354         C c;
1355 
1356         @serdeProxy!E
1357         int d;
1358     }
1359 
1360     import std.meta: AliasSeq;
1361     static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A));
1362 }
1363 
1364 /++
1365 Serialization member final proxy type
1366 +/
1367 template serdeFinalSerializationMemberType(T, string member)
1368 {
1369     import std.traits: hasUDA;
1370     static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
1371     {
1372         alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
1373     }
1374     else
1375     {
1376         alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeSerializationMemberType!(T, member));
1377     }
1378 }
1379 
1380 /// ditto
1381 template serdeFinalSerializationMemberType(T)
1382 {
1383     ///
1384     alias serdeFinalSerializationMemberType(string member) = .serdeFinalSerializationMemberType!(T, member);
1385 }
1386 
1387 ///
1388 version(mir_test) unittest
1389 {
1390 
1391     static struct A
1392     {
1393         
1394     }
1395 
1396     @serdeProxy!A
1397     static struct B {}
1398 
1399     @serdeProxy!B
1400     static struct C {}
1401 
1402 
1403     @serdeProxy!double
1404     struct E {}
1405 
1406     struct D
1407     {
1408         C c;
1409 
1410         @serdeProxy!E
1411         int d;
1412     }
1413 
1414     static assert (is(serdeFinalSerializationMemberType!(D, "c") == A), serdeFinalSerializationMemberType!(D, "c"));
1415     static assert (is(serdeFinalSerializationMemberType!(D, "d") == double));
1416 }
1417 
1418 /++
1419 Serialization members final proxy types
1420 +/
1421 template serdeSerializationFinalProxyMemberTypes(T)
1422 {
1423     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
1424     alias serdeSerializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalProxySerializableMembers!T))));
1425 }
1426 
1427 ///
1428 version(mir_test) unittest
1429 {
1430 
1431     static struct A {}
1432 
1433     @serdeProxy!A
1434     static struct B {}
1435 
1436     @serdeProxy!B
1437     static struct C {}
1438 
1439     @serdeProxy!B
1440     static struct E {}
1441 
1442     static struct D
1443     {
1444         C c;
1445 
1446         @serdeProxy!E
1447         int d;
1448     }
1449 
1450     import std.meta: AliasSeq;
1451     static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A));
1452 }
1453 
1454 /++
1455 Deserialization members final deep proxy types
1456 +/
1457 template serdeDeserializationFinalDeepProxyMemberTypes(T)
1458 {
1459     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
1460     import mir.algebraic: isVariant;
1461     static if (isVariant!T)
1462         alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes));
1463     else
1464     static if (isAlgebraicAliasThis!T)
1465     {
1466         T* aggregate;
1467         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
1468         alias serdeDeserializationFinalDeepProxyMemberTypes = .serdeDeserializationFinalDeepProxyMemberTypes!A;
1469     }
1470     else
1471         alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T))));
1472 }
1473 
1474 ///
1475 version(mir_test) unittest
1476 {
1477 
1478     static struct A {}
1479 
1480     @serdeProxy!(A[])
1481     static struct B {}
1482 
1483     enum R {a, b, c}
1484 
1485     @serdeProxy!(B[R])
1486     static struct C {}
1487 
1488     @serdeProxy!(B[string])
1489     static struct E {}
1490 
1491     static struct D
1492     {
1493         C c;
1494 
1495         @serdeProxy!E
1496         int d;
1497     }
1498 
1499     import std.meta: AliasSeq;
1500     static assert (is(serdeDeserializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeDeserializationFinalDeepProxyMemberTypes!D);
1501 }
1502 
1503 /++
1504 Serialization members final deep proxy types
1505 +/
1506 template serdeSerializationFinalDeepProxyMemberTypes(T)
1507 {
1508     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
1509     import mir.algebraic: isVariant;
1510     static if (isVariant!T)
1511         alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes));
1512     else
1513     static if (isAlgebraicAliasThis!T)
1514     {
1515         T* aggregate;
1516         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
1517         alias serdeSerializationFinalDeepProxyMemberTypes = .serdeSerializationFinalDeepProxyMemberTypes!A;
1518     }
1519     else
1520         alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T))));
1521 }
1522 
1523 ///
1524 version(mir_test) unittest
1525 {
1526 
1527     static struct A {}
1528 
1529     @serdeProxy!(A[])
1530     static struct B {}
1531 
1532     enum R {a, b, c}
1533 
1534     @serdeProxy!(B[R])
1535     static struct C {}
1536 
1537     @serdeProxy!(B[string])
1538     static struct E {}
1539 
1540     static struct D
1541     {
1542         C c;
1543 
1544         @serdeProxy!E
1545         int d;
1546     }
1547 
1548     import std.meta: AliasSeq;
1549     static assert (is(serdeSerializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalDeepProxyMemberTypes!D);
1550 }
1551 
1552 private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...)
1553 {
1554     import std.meta: NoDuplicates, staticMap;
1555     alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalProxyMemberTypes, T));
1556     static if (F.length == T.length)
1557         alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = T;
1558     else
1559         alias serdeDeserializationFinalProxyMemberTypesRecurseImpl  = .serdeDeserializationFinalProxyMemberTypesRecurseImpl!F;
1560 }
1561 
1562 /++
1563 Deserialization members final proxy types (recursive)
1564 +/
1565 alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserializationFinalProxyMemberTypesRecurseImpl!(serdeGetFinalProxy!T);
1566 
1567 ///
1568 version(mir_test) unittest
1569 {
1570 
1571     static struct A { double g; }
1572 
1573     @serdeProxy!A
1574     static struct B {}
1575 
1576     @serdeProxy!B
1577     static struct C {}
1578 
1579     @serdeProxy!B
1580     static struct E {}
1581 
1582     static struct D
1583     {
1584         C c;
1585 
1586         @serdeProxy!E
1587         int d;
1588     }
1589 
1590     @serdeProxy!D
1591     static struct F {}
1592 
1593     import std.meta: AliasSeq;
1594     static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
1595 }
1596 
1597 private template serdeSerializationFinalDeepProxyMemberTypesRecurseImpl(T...)
1598 {
1599     import std.meta: NoDuplicates, staticMap;
1600     alias F = NoDuplicates!(T, staticMap!(serdeSerializationFinalDeepProxyMemberTypes, T));
1601     static if (F.length == T.length)
1602         alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = T;
1603     else
1604         alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl  = .serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!F;
1605 }
1606 
1607 private template serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl(T...)
1608 {
1609     import std.meta: NoDuplicates, staticMap;
1610     alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalDeepProxyMemberTypes, T));
1611     static if (F.length == T.length)
1612         alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = T;
1613     else
1614         alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl  = .serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!F;
1615 }
1616 
1617 /++
1618 Deserialization members final deep proxy types (recursive)
1619 +/
1620 alias serdeDeserializationFinalDeepProxyMemberTypesRecurse(T) = serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T);
1621 
1622 ///
1623 version(mir_test) unittest
1624 {
1625 
1626     static struct A { double g; }
1627 
1628     @serdeProxy!(A[])
1629     static struct B {}
1630 
1631     @serdeProxy!(B[string])
1632     static struct C {}
1633 
1634     @serdeProxy!B
1635     static struct E {}
1636 
1637     static struct D
1638     {
1639         C c;
1640 
1641         @serdeProxy!(E[])
1642         int d;
1643     }
1644 
1645     @serdeProxy!D
1646     static struct F {}
1647 
1648     import std.meta: AliasSeq;
1649     static assert (is(serdeDeserializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
1650 }
1651 
1652 /++
1653 Serialization members final deep proxy types (recursive)
1654 +/
1655 alias serdeSerializationFinalDeepProxyMemberTypesRecurse(T) = serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T);
1656 
1657 ///
1658 version(mir_test) unittest
1659 {
1660 
1661     static struct A { double g; }
1662 
1663     @serdeProxy!(A[])
1664     static struct B {}
1665 
1666     @serdeProxy!(B[string])
1667     static struct C {}
1668 
1669     @serdeProxy!B
1670     static struct E {}
1671 
1672     static struct D
1673     {
1674         C c;
1675 
1676         @serdeProxy!(E[])
1677         int d;
1678     }
1679 
1680     @serdeProxy!D
1681     static struct F {}
1682 
1683     import std.meta: AliasSeq;
1684     static assert (is(serdeSerializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)), serdeSerializationFinalDeepProxyMemberTypesRecurse!F);
1685 }
1686 
1687 package string[] sortUniqKeys()(string[] keys)
1688     @safe pure nothrow
1689 {
1690     import mir.algorithm.iteration: uniq;
1691     import mir.array.allocation: array;
1692     import mir.ndslice.sorting: sort;
1693 
1694     return keys
1695         .sort!((a, b) {
1696             if (sizediff_t d = a.length - b.length)
1697                 return d < 0;
1698             return a < b;
1699         })
1700         .uniq
1701         .array;
1702 }
1703 
1704 
1705 private template serdeGetKeysIn2(T)
1706 {
1707     // T* value;
1708     enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(__traits(getMember, T, member));
1709 }
1710 
1711 private template serdeGetKeyOut2(T)
1712 {
1713     enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) is null ? null : [serdeGetKeyOut!(__traits(getMember, T, member))];
1714 }
1715 
1716 private template serdeFinalDeepProxyDeserializableMemberKeys(T)
1717 {
1718     import std.meta: staticMap, aliasSeqOf;
1719     import std.traits: isAggregateType;
1720 
1721     static if (isAggregateType!T)
1722     {
1723         import mir.algebraic: isVariant;
1724         static if (isVariant!T)
1725             enum string[] serdeFinalDeepProxyDeserializableMemberKeys = getAlgebraicAnnotationsOfVariant!T;
1726         else
1727             enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))];
1728     }
1729     else
1730     static if (is(T == enum))
1731     {
1732         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = enumAllKeysIn!T;
1733     }
1734     else
1735         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
1736 }
1737 
1738 package template getAlgebraicAnnotationsOfVariant(T)
1739 {
1740     import std.meta: staticMap, Filter;
1741     enum string[] getAlgebraicAnnotationsOfVariant = [staticMap!(serdeGetAlgebraicAnnotation, Filter!(serdeHasAlgebraicAnnotation, T.AllowedTypes))];
1742 }
1743 
1744 private template serdeFinalDeepProxySerializableMemberKeys(T)
1745 {
1746     import std.meta: staticMap, aliasSeqOf;
1747     import std.traits: isAggregateType;
1748 
1749     static if (isAggregateType!T)
1750     {
1751         import mir.algebraic: isVariant;
1752         static if (isVariant!T)
1753             enum string[] serdeFinalDeepProxySerializableMemberKeys = getAlgebraicAnnotationsOfVariant!T;
1754         else
1755             enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))];
1756     }
1757     else
1758     static if (is(T == enum))
1759     {
1760         enum string[] serdeFinalDeepProxySerializableMemberKeys = enumAllKeysOut!T;
1761     }
1762     else
1763         enum string[] serdeFinalDeepProxySerializableMemberKeys = null;
1764 }
1765 
1766 private template serdeGetAlgebraicAnnotations(T)
1767 {
1768     static if (isAggregateType!T || is(T == enum))
1769         static if (hasUDA!(T, serdeAlgebraicAnnotation))
1770             enum string[] serdeGetAlgebraicAnnotations = [getUDA!(T, serdeAlgebraicAnnotation).annotation];
1771         else
1772             enum string[] serdeGetAlgebraicAnnotations = null;
1773     else
1774         enum string[] serdeGetAlgebraicAnnotations = null;
1775 }
1776 
1777 package template serdeIsComplexVariant(T)
1778 {
1779     import mir.algebraic: isVariant, isNullable;
1780     static if (isVariant!T)
1781     {
1782         enum serdeIsComplexVariant = (T.AllowedTypes.length - isNullable!T) > 1;
1783     }
1784     else
1785     {
1786         enum bool serdeIsComplexVariant = false;
1787     }
1788 }
1789 
1790 package template isAlgebraicAliasThis(T)
1791 {
1792     static if (__traits(getAliasThis, T).length)
1793     {
1794         import mir.algebraic: isVariant;
1795         T* aggregate;
1796         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
1797         enum isAlgebraicAliasThis = isVariant!A;
1798     }
1799     else
1800     {
1801         enum isAlgebraicAliasThis = false;
1802     }
1803 }
1804 
1805 /++
1806 Serialization members final proxy keys (recursive)
1807 +/
1808 template serdeGetSerializationKeysRecurse(T)
1809 {
1810     import std.meta: staticMap, aliasSeqOf;
1811     enum string[] serdeGetSerializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxySerializableMemberKeys, serdeSerializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
1812 }
1813 
1814 ///
1815 version(mir_test) unittest
1816 {
1817     enum Y
1818     {
1819         a,
1820         b,
1821         c,
1822     }
1823 
1824     static struct A { double g; float d; }
1825 
1826     @serdeProxy!A
1827     static struct B {  int f; }
1828 
1829     @serdeProxy!(B[Y][string])
1830     static union C {  int f; }
1831 
1832     @serdeProxy!(B[])
1833     static interface E {  int f() @property; }
1834 
1835     enum N { a, b }
1836 
1837     static class D
1838     {
1839         C c;
1840 
1841         @serdeProxy!(E[])
1842         int d;
1843 
1844         N e;
1845     }
1846 
1847     @serdeAlgebraicAnnotation("$F")
1848     @serdeProxy!D
1849     static struct F { int f; }
1850 
1851     static assert (serdeGetSerializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]);
1852 
1853     import mir.algebraic;
1854     static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]);
1855 }
1856 
1857 /++
1858 Deserialization members final proxy keys (recursive)
1859 +/
1860 template serdeGetDeserializationKeysRecurse(T)
1861 {
1862     import std.meta: staticMap, aliasSeqOf;
1863     enum string[] serdeGetDeserializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
1864 }
1865 
1866 ///
1867 version(mir_test) unittest
1868 {
1869 
1870     static struct A { double g; float d; }
1871 
1872     @serdeProxy!A
1873     static struct B {  int f; }
1874 
1875     @serdeProxy!(B[string])
1876     static union C {  int f; }
1877 
1878     @serdeProxy!(B[])
1879     static interface E {  int f() @property; }
1880 
1881     enum N { a, b }
1882 
1883     static class D
1884     {
1885         C c;
1886 
1887         @serdeProxy!(E[])
1888         int d;
1889 
1890         N e;
1891     }
1892 
1893     @serdeAlgebraicAnnotation("$F")
1894     @serdeProxy!D
1895     static struct F { int f; }
1896 
1897     static assert (serdeGetDeserializationKeysRecurse!N == ["a", "b"], serdeGetDeserializationKeysRecurse!N);
1898 
1899     static assert (serdeGetDeserializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]);
1900 
1901     import mir.algebraic;
1902     static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]);
1903 }
1904 
1905 /++
1906 UDA used to force deserializer to initilize members in the order of their definition in the target object/structure.
1907 
1908 The attribute force deserializer to create a dummy type (recursively), initializer its fields and then assign them to
1909 to the object members (fields and setters) in the order of their definition.
1910 
1911 See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn), $(LREF serdeIgnoreInIfAggregate).
1912 +/
1913 enum serdeOrderedIn;
1914 
1915 /++
1916 UDA used to force deserializer to initilize members in the order of their definition in the target object/structure.
1917 
1918 Unlike $(LREF serdeOrderedIn) `serdeRealOrderedDummy` force deserialzier to iterate all DOM keys for each object deserialization member.
1919 It is slower but more universal approach.
1920 
1921 See_also: $(LREF serdeOrderedIn), $(LREF serdeIgnoreInIfAggregate)
1922 +/
1923 enum serdeRealOrderedIn;
1924 
1925 /++
1926 UDA used to force deserializer to skip the member final deserialization.
1927 A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method
1928 and dummy method `serdeFinalizeTargetMember`.
1929 +/
1930 enum serdeFromDummyByUser;
1931 
1932 /++
1933 UDA used to force serializer to output members in the alphabetical order of their output keys.
1934 +/
1935 enum serdeAlphabetOut;
1936 
1937 /++
1938 A dummy structure usefull $(LREF serdeOrderedIn) support.
1939 +/
1940 struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
1941     if (is(serdeGetFinalProxy!T == T) && isAggregateType!T)
1942 {
1943     import std.traits: hasUDA;
1944 
1945     @serdeIgnore
1946     SerdeFlags!(typeof(this)) __serdeFlags;
1947 
1948     static if (__optionalByDefault)
1949         alias __serdeOptionalRequired = serdeRequired;
1950     else
1951         alias __serdeOptionalRequired = serdeOptional;
1952 
1953     this()(T value)
1954     {
1955         static foreach (member; serdeFinalProxyDeserializableMembers!T)
1956         {
1957             static if (hasField!(T, member))
1958             {
1959                 static if (__traits(compiles, {__traits(getMember, this, member) = __traits(getMember, value, member);}))
1960                     __traits(getMember, this, member) = __traits(getMember, value, member);
1961             }
1962         }
1963     }
1964 
1965 public:
1966 
1967     static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
1968     {
1969         static if (hasField!(T, member))
1970         {
1971             static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
1972             {
1973                 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
1974             }
1975             else
1976             static if (isAggregateType!(typeof(__traits(getMember, T, member))))
1977             {
1978                 static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
1979                 {
1980                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
1981                 }
1982                 else
1983                 static if (__traits(compiles, {
1984                     mixin("enum SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");");
1985                 }))
1986                 {
1987                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");");
1988                 }
1989                 else
1990                 {
1991                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ ";");
1992                 }
1993             }
1994             else
1995             {
1996                 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
1997             }
1998         }
1999         else
2000         {
2001             mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
2002         }
2003     }
2004 
2005     /// Initialize target members
2006     void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags)
2007     {
2008         __serdeFlags = flags;
2009     }
2010 
2011     /// Initialize target members
2012     void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags)
2013     {
2014         import std.traits: hasElaborateAssign;
2015         static foreach (member; serdeFinalProxyDeserializableMembers!T)
2016             __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member);
2017         static foreach (member; serdeFinalProxyDeserializableMembers!T)
2018             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
2019         {{
2020             if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
2021             {
2022                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
2023                 {
2024                     alias M = typeof(__traits(getMember, value, member));
2025                     SerdeFlags!M memberFlags;
2026                     __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
2027                     static if (__traits(hasMember, M, "serdeFinalizeWithFlags"))
2028                     {
2029                         __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
2030                     }
2031                     static if (__traits(hasMember, M, "serdeFinalize"))
2032                     {
2033                         __traits(getMember, value, member).serdeFinalize();
2034                     }
2035                 }
2036                 else
2037                 {
2038                     static if (hasElaborateAssign!(typeof(__traits(getMember, this, member))))
2039                     {
2040                         import core.lifetime: move;
2041                         __traits(getMember, value, member) = move(__traits(getMember, this, member));
2042                     }
2043                     else
2044                         __traits(getMember, value, member) = __traits(getMember, this, member);
2045                 }
2046             }
2047         }}
2048         static if (__traits(hasMember, T, "serdeFinalizeWithDummy"))
2049         {
2050             value.serdeFinalizeWithDummy(this);
2051         }
2052     }
2053 
2054     /// Initialize target member
2055     void serdeFinalizeTargetMember(string member)(ref T value)
2056     {
2057             if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
2058             {
2059                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
2060                 {
2061                     alias M = typeof(__traits(getMember, value, member));
2062                     SerdeFlags!M memberFlags;
2063                     __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
2064                     static if (__traits(hasMember, M, "serdeFinalizeWithFlags"))
2065                     {
2066                         __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
2067                     }
2068                     static if (__traits(hasMember, M, "serdeFinalize"))
2069                     {
2070                         __traits(getMember, value, member).serdeFinalize();
2071                     }
2072                 }
2073                 else
2074                 {
2075                     static if (hasElaborateAssign!(typeof(__traits(getMember, this, member))))
2076                     {
2077                         import core.lifetime: move;
2078                         __traits(getMember, value, member) = move(__traits(getMember, this, member));
2079                     }
2080                     else
2081                         __traits(getMember, value, member) = __traits(getMember, this, member);
2082                 }
2083             }
2084     }
2085 }
2086 
2087 ///
2088 version(mir_test) unittest
2089 {
2090     import std.traits;
2091 
2092     static struct S
2093     {
2094         private double _d;
2095 
2096         @serdeProxy!int
2097         void d(double v) @property { _d = v; }
2098 
2099         string s;
2100     }
2101 
2102     static assert(is(typeof(SerdeOrderedDummy!S.init.d) == double), SerdeOrderedDummy!S.init.d);
2103     static assert(is(typeof(SerdeOrderedDummy!S.init.s) == string));
2104     static assert(hasUDA!(S.d, serdeProxy));
2105     static assert(hasUDA!(SerdeOrderedDummy!S.d, serdeProxy));
2106 }
2107 
2108 /++
2109 A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method.
2110 +/
2111 struct SerdeFlags(T)
2112 {
2113     static if (is(T : SerdeOrderedDummy!I, I))
2114         static foreach(member; serdeFinalProxyDeserializableMembers!I)
2115             mixin("bool " ~ member ~ ";");
2116     else
2117         static foreach(member; serdeFinalProxyDeserializableMembers!T)
2118             mixin("bool " ~ member ~ ";");
2119 }
2120         
2121 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
2122 {
2123     ///
2124     SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context context)
2125     {
2126         import core.lifetime: move;
2127         import mir.conv: to;
2128 
2129         enum likeList = hasUDA!(__traits(getMember, value, member), serdeLikeList);
2130         enum likeStruct  = hasUDA!(__traits(getMember, value, member), serdeLikeStruct);
2131         enum hasProxy = hasUDA!(__traits(getMember, value, member), serdeProxy);
2132         enum hasScoped = hasUDA!(__traits(getMember, value, member), serdeScoped);
2133 
2134         static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes");
2135         static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
2136         static assert (hasProxy >= likeList, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
2137 
2138         alias Member = serdeDeserializationMemberType!(T, member);
2139 
2140         static if (hasProxy)
2141             alias Temporal = serdeGetProxy!(__traits(getMember, value, member));
2142         else
2143             alias Temporal = Member;
2144 
2145         static if (hasScoped)
2146             static if (__traits(compiles, { Temporal temporal; deserializeScoped(data, temporal); }))
2147                 alias impl = deserializeScoped;
2148             else
2149                 alias impl = deserializeValue;
2150         else
2151             alias impl = deserializeValue;
2152 
2153         static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed.");
2154 
2155         static if (!hasUDA!(__traits(getMember, value, member), serdeAllowMultiple))
2156             if (__traits(getMember, requiredFlags, member))
2157                 return excm!member;
2158 
2159         __traits(getMember, requiredFlags, member) = true;
2160 
2161         static if (likeList)
2162         {
2163             foreach(elem; data.byElement)
2164             {
2165                 Temporal temporal;
2166                 if (auto exc = impl(elem, temporal, context))
2167                     return exc;
2168                 __traits(getMember, value, member).put(move(temporal));
2169             }
2170         }
2171         else
2172         static if (likeStruct)
2173         {
2174             foreach(v; data.byKeyValue(context))
2175             {
2176                 Temporal temporal;
2177                 if (auto exc = impl(v.value, temporal, context))
2178                     return exc;
2179                 __traits(getMember, value, member)[v.key.idup] = move(temporal);
2180             }
2181         }
2182         else
2183         static if (hasProxy)
2184         {
2185             Temporal temporal;
2186             if (auto exc = impl(data, temporal, context))
2187                 return exc;
2188             __traits(getMember, value, member) = to!(serdeDeserializationMemberType!(T, member))(move(temporal));
2189         }
2190         else
2191         static if (hasField!(T, member))
2192         {
2193             if (auto exc = impl(data, __traits(getMember, value, member), context))
2194                 return exc;
2195         }
2196         else
2197         {
2198             Member temporal;
2199             if (auto exc = impl(data, temporal, context))
2200                 return exc;
2201             __traits(getMember, value, member) = move(temporal);
2202         }
2203 
2204         static if (hasUDA!(__traits(getMember, value, member), serdeTransformIn))
2205         {
2206             alias transform = serdeGetTransformIn!(__traits(getMember, value, member));
2207             static if (hasField!(T, member))
2208             {
2209                 transform(__traits(getMember, value, member));
2210             }
2211             else
2212             {
2213                 auto temporal = __traits(getMember, value, member);
2214                 transform(temporal);
2215                 __traits(getMember, value, member) = move(temporal);
2216             }
2217         }
2218 
2219         return null;
2220     }
2221 }
2222 
2223 private:
2224 
2225 auto fastLazyToUpper()(const(char)[] name)
2226 {
2227     import mir.ndslice.topology: map;
2228     return name.map!fastToUpper;
2229 }
2230 
2231 auto fastToUpper()(char a)
2232 {   // std.ascii may not be inlined
2233     return 'a' <= a && a <= 'z' ? cast(char)(a ^ 0x20) : a;
2234 }
2235 
2236 @safe pure nothrow @nogc
2237 char[] fastToUpperInPlace()(scope return char[] a)
2238 {
2239     foreach(ref char e; a)
2240         e = e.fastToUpper;
2241     return a;
2242 }
2243 
2244 template enumAllKeysIn(T)
2245     if (is(T == enum))
2246 {
2247     import std.traits: EnumMembers;
2248     import std.meta: staticMap, aliasSeqOf;
2249     enum string[] enumAllKeysIn = [staticMap!(aliasSeqOf, staticMap!(.serdeGetKeysIn, EnumMembers!T))];
2250 }
2251 
2252 template enumAllKeysOut(T)
2253     if (is(T == enum))
2254 {
2255     import std.traits: EnumMembers;
2256     import std.meta: staticMap, aliasSeqOf;
2257     enum string[] enumAllKeysOut = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
2258 }