The OpenD Programming Language

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