1 /++
2 $(H4 High level serialization API)
3 
4 Macros:
5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP)
6 Authros: Ilia Ki
7 +/
8 module mir.ser;
9 
10 import mir.conv;
11 import mir.deser;
12 import mir.ion.internal.basic_types;
13 import mir.ion.type_code;
14 import mir.reflection;
15 import std.meta;
16 import std.traits;
17 import mir.internal.meta: hasUDA, getUDAs;
18 import mir.exception: toMutable;
19 
20 public import mir.serde;
21 
22 static immutable cannotSerializeVoidMsg = "Can't serialize none (void) value of the algebraic type";
23 version (D_Exceptions)
24     private static immutable cannotSerializeVoid = new Exception(cannotSerializeVoidMsg);
25 
26 private noreturn serializeVoidHandler() @safe pure @nogc
27 {
28     version (D_Exceptions)
29         throw cannotSerializeVoid.toMutable;
30     else
31         assert(0, cannotSerializeVoidMsg);
32 }
33 
34 private noreturn serializeVoidHandlerWithSerializer(S)(scope ref S serializer) @safe pure @nogc
35 {
36     version (D_Exceptions)
37         throw cannotSerializeVoid.toMutable;
38     else
39         assert(0, cannotSerializeVoidMsg);
40 }
41 
42 private noreturn serializeVoidHandlerWithSerializerAndState(S)(scope ref S serializer, size_t state) @safe pure @nogc
43 {
44     version (D_Exceptions)
45         throw cannotSerializeVoid.toMutable;
46     else
47         assert(0, cannotSerializeVoidMsg);
48 }
49 
50 /// `null` value serialization
51 void serializeValue(S)(scope ref S serializer, typeof(null))
52 {
53     serializer.putValue(null);
54 }
55 
56 ///
57 version(mir_ion_test)
58 unittest
59 {
60     import mir.ser.json: serializeJson;
61     assert(serializeJson(null) == `null`, serializeJson(null));
62 }
63 
64 /// Number serialization
65 void serializeValue(S, V)(scope ref S serializer, const V value)
66     if (isNumeric!V && !is(V == enum))
67 {
68     serializer.putValue(value);
69 }
70 
71 ///
72 version(mir_ion_test)
73 unittest
74 {
75     import mir.ser.json: serializeJson;
76 
77     assert(serializeJson(2.40f) == `2.4`);
78     assert(serializeJson(float.nan) == `"nan"`);
79     assert(serializeJson(float.infinity) == `"+inf"`);
80     assert(serializeJson(-float.infinity) == `"-inf"`);
81 }
82 
83 /// Boolean serialization
84 void serializeValue(S, V)(scope ref S serializer, scope const V value)
85     if (is(V == bool) && !is(V == enum))
86 {
87     serializer.putValue(value);
88 }
89 
90 /// Char serialization
91 void serializeValue(S, V : char)(scope ref S serializer, scope const V value)
92     if (is(V == char) && !is(V == enum))
93 {
94     char[1] v = value;
95     serializer.putValue(v[]);
96 }
97 
98 ///
99 version(mir_ion_test)
100 unittest
101 {
102     import mir.ser.json: serializeJson;
103     assert(serializeJson(true) == `true`);
104 }
105 
106 /// Enum serialization
107 void serializeValue(S, V)(scope ref S serializer, scope const V value)
108     if(is(V == enum))
109 {
110     static if (hasUDA!(V, serdeProxy))
111     {
112         serializeProxyCastImpl!(S, V)(serializer, value);
113     }
114     else
115     {
116         serializer.putSymbol(serdeGetKeyOut(value));
117     }
118 }
119 
120 ///
121 version(mir_ion_test)
122 unittest
123 {
124     import mir.ser.json: serializeJson;
125     import mir.ser.ion: serializeIon;
126     import mir.ser.text: serializeText;
127     import mir.deser.ion: deserializeIon;
128     import mir.small_string;
129     import mir.rc.array;
130     enum Key { bar, @serdeKeys("FOO", "foo") foo }
131     assert(serializeJson(Key.foo) == `"FOO"`);
132     assert(serializeText(Key.foo) == `FOO`);
133     assert(serializeIon(Key.foo).deserializeIon!Key == Key.foo);
134     assert(serializeIon(Key.foo).deserializeIon!string == "FOO");
135     assert(serializeIon(Key.foo).deserializeIon!(SmallString!32) == "FOO");
136     auto rcstring = serializeIon(Key.foo).deserializeIon!(RCArray!char);
137     assert(rcstring[] == "FOO");
138 }
139 
140 private static void serializeProxyCastImpl(S, alias U, V)(scope ref S serializer, scope const V value)
141 {
142     static if (hasUDA!(U, serdeProxyCast))
143     {
144         scope casted = cast(serdeGetProxy!U)value;
145         serializeValue(serializer, casted);
146     }
147     else
148         serializer.serializeWithProxy!(serdeGetProxy!U)(value);
149 }
150 
151 /// String serialization
152 void serializeValue(S)(scope ref S serializer, scope const(char)[] value)
153 {
154     if(value is null)
155     {
156         serializer.putNull(IonTypeCode..string);
157         return;
158     }
159     serializer.putValue(value);
160 }
161 
162 ///
163 version(mir_ion_test)
164 unittest
165 {
166     import mir.ser.json: serializeJson;
167     assert(serializeJson("\t \" \\") == `"\t \" \\"`, serializeJson("\t \" \\"));
168 }
169 
170 /// Array serialization
171 void serializeValue(S, T)(scope ref S serializer, scope const T[] value) @safe
172     if(!isSomeChar!T)
173 {
174     if (value is null)
175     {
176         serializer.putNull(IonTypeCode.list);
177         return;
178     }
179     auto state = serializer.beginList(value);
180     foreach (ref elem; value)
181     {
182         serializer.elemBegin;
183         serializer.serializeValue(elem);
184     }
185     serializer.listEnd(state);
186 }
187 
188 private enum isRefIterable(T) = __traits(compiles, (ref T value) { foreach (ref elem; value) {}  });
189 
190 version(mir_ion_test)
191 unittest
192 {
193     static assert(isRefIterable!(double[]));
194 }
195 
196 package template hasLikeList(T)
197 {
198     import mir.serde: serdeLikeList;
199     static if (is(T == enum) || isAggregateType!T)
200         enum hasLikeList = hasUDA!(T, serdeLikeList);
201     else
202         enum hasLikeList = false;
203 }
204 
205 package template hasProxy(T)
206 {
207     import mir.serde: serdeProxy;
208     static if (is(T == enum) || isAggregateType!T)
209         enum hasProxy = hasUDA!(T, serdeProxy);
210     else
211         enum hasProxy = false;
212 }
213 
214 package template hasFields(T)
215 {
216     import mir.serde: serdeFields;
217     static if (is(T == enum) || isAggregateType!T)
218         enum hasFields = hasUDA!(T, serdeFields);
219     else
220         enum hasFields = false;
221 }
222 
223 
224 /// input range serialization
225 version(mir_ion_test)
226 unittest
227 {
228     import mir.algorithm.iteration : filter;
229 
230     static struct Foo
231     {
232         int i;
233     }
234 
235     auto ar = [Foo(1), Foo(3), Foo(4), Foo(17)];
236 
237     auto filtered1 = ar.filter!"a.i & 1";
238     auto filtered2 = ar.filter!"!(a.i & 1)";
239 
240     import mir.ser.json: serializeJson;
241     assert(serializeJson(filtered1) == `[{"i":1},{"i":3},{"i":17}]`);
242     assert(serializeJson(filtered2) == `[{"i":4}]`);
243 }
244 
245 ///
246 unittest
247 {
248     import mir.ser.json: serializeJson;
249     uint[2] ar = [1, 2];
250     assert(serializeJson(ar) == `[1,2]`);
251     assert(serializeJson(ar[]) == `[1,2]`);
252     assert(serializeJson(ar[0 .. 0]) == `[]`);
253     assert(serializeJson((uint[]).init) == `[]`);
254 }
255 
256 /// String-value associative array serialization
257 void serializeValue(S, T)(scope ref S serializer, scope const T[string] value)
258 {
259     if(value is null)
260     {
261         serializer.putNull(IonTypeCode.struct_);
262         return;
263     }
264     auto state = serializer.beginStruct(value);
265     foreach (key, ref const val; (()@trusted => cast()value)())
266     {
267         serializer.putKey(key);
268         serializer.serializeValue(val);
269     }
270     serializer.structEnd(state);
271 }
272 
273 ///
274 version(mir_ion_test)
275 unittest
276 {
277     import mir.ser.json: serializeJson;
278     import mir.ser.text: serializeText;
279     uint[string] ar = ["a" : 1];
280     auto car = cast(const)ar;
281     assert(serializeJson(car) == `{"a":1}`);
282     assert(serializeText(ar) == `{a:1}`);
283     ar.remove("a");
284     assert(serializeJson(ar) == `{}`);
285     assert(serializeJson((uint[string]).init) == `{}`);
286 }
287 
288 /// Enumeration-value associative array serialization
289 void serializeValue(S, V : const T[K], T, K)(scope ref S serializer, scope const V value)
290     if(is(K == enum))
291 {
292     if(value is null)
293     {
294         serializer.putNull(IonTypeCode.struct_);
295         return;
296     }
297     auto state = serializer.beginStruct(value);
298     foreach (key, ref val; value)
299     {
300         serializer.putKey(serdeGetKeyOut(key));
301         serializer.serializeValue(val);
302     }
303     serializer.structEnd(state);
304 }
305 
306 ///
307 version(mir_ion_test)
308 unittest
309 {
310     import mir.ser.json: serializeJson;
311     enum E { a, b }
312     uint[E] ar = [E.a : 1];
313     auto car = cast(const)ar;
314     assert(serializeJson(car) == `{"a":1}`);
315     ar.remove(E.a);
316     assert(serializeJson(ar) == `{}`);
317     assert(serializeJson((uint[string]).init) == `{}`);
318 }
319 
320 /// integral typed value associative array serialization
321 void serializeValue(S,  V : const T[K], T, K)(scope ref S serializer, scope const V value)
322     if (isIntegral!K && !is(K == enum))
323 {
324     if(value is null)
325     {
326         serializer.putNull(IonTypeCode.struct_);
327         return;
328     }
329     auto state = serializer.beginStruct(value);
330     foreach (key, ref val; value)
331     {
332         import mir.format: print, stringBuf;
333         import mir.small_string : SmallString;
334         auto buffer = stringBuf;
335         print(buffer, key);
336         serializer.putKey(buffer.data);
337         .serializeValue(serializer, val);
338     }
339     serializer.structEnd(state);
340 }
341 
342 ///
343 version(mir_ion_test)
344 unittest
345 {
346     import mir.ser.json: serializeJson;
347     uint[short] ar = [256 : 1];
348     assert(serializeJson(ar) == `{"256":1}`);
349     ar.remove(256);
350     assert(serializeJson(ar) == `{}`);
351     assert(serializeJson((uint[string]).init) == `{}`);
352     // assert(deserializeJson!(uint[short])(`{"256":1}`) == cast(uint[short]) [256 : 1]);
353 }
354 
355 
356 private IonTypeCode nullTypeCodeOf(T)()
357 {
358     import mir.algebraic: isVariant;
359     import mir.serde: serdeGetFinalProxy;
360     import std.traits: Unqual;
361 
362     IonTypeCode code;
363 
364     alias U = Unqual!T;
365 
366     static if (is(U == bool))
367         code = IonTypeCode.bool_;
368     else
369     static if (isUnsigned!U)
370         code = IonTypeCode.uInt;
371     else
372     static if (isIntegral!U || isBigInt!U)
373         code = IonTypeCode.nInt;
374     else
375     static if (isFloatingPoint!U)
376         code = IonTypeCode.float_;
377     else
378     static if (isDecimal!U)
379         code = IonTypeCode.decimal;
380     else
381     static if (isTuple!U)
382         code = IonTypeCode.list;
383     else
384     static if (isClob!U)
385         code = IonTypeCode.clob;
386     else
387     static if (isBlob!U)
388         code = IonTypeCode.blob;
389     else
390     static if (isSomeString!U)
391         code = IonTypeCode..string;
392     else
393     static if (!isVariant!U && isAggregateType!U)
394     {
395         static if (hasUDA!(U, serdeProxy))
396             code = .nullTypeCodeOf!(serdeGetFinalProxy!U);
397         else
398         static if (isIterable!U && !hasFields!U)
399             code = IonTypeCode.list;
400         else
401             code = IonTypeCode.struct_;
402     }
403     else
404     static if (isIterable!U && !hasFields!U)
405         code = IonTypeCode.list;
406 
407     return code;
408 }
409 
410 version(mir_ion_test)
411 unittest
412 {
413     static assert(nullTypeCodeOf!long == IonTypeCode.nInt);
414 }
415 
416 @safe
417 private void serializeAnnotatedValue(S, V)(scope ref S serializer, scope ref const V value, size_t wrapperState)
418 {
419     import mir.algebraic: Algebraic;
420     static if (serdeGetAnnotationMembersOut!V.length)
421     {
422         static foreach (annotationMember; serdeGetAnnotationMembersOut!V)
423         {
424             static if (is(typeof(__traits(getMember, value, annotationMember)) == enum))
425                 serializer.putAnnotation(serdeGetKeyOut(__traits(getMember, value, annotationMember)));
426             else
427             static if (__traits(compiles, serializer.putAnnotation(__traits(getMember, value, annotationMember)[])))
428                 serializer.putAnnotation(__traits(getMember, value, annotationMember)[]);
429             else
430                 foreach (annotation; __traits(getMember, value, annotationMember))
431                     serializer.putAnnotation(annotation);
432         }
433     }
434 
435     static if (isAlgebraicAliasThis!V || isAnnotated!V)
436     {
437         static if (__traits(getAliasThis, V).length == 1)
438             enum aliasThisMember = __traits(getAliasThis, V)[0];
439         else
440             enum aliasThisMember = "value";
441         return serializeAnnotatedValue(serializer, __traits(getMember, value, aliasThisMember), wrapperState);
442     }
443     else
444     static if (is(V == Algebraic!TypeSet, TypeSet...) && (!isStdNullable!V || Algebraic!TypeSet.AllowedTypes.length != 2))
445     {
446         import mir.algebraic: match;
447         match!(
448             serializeVoidHandlerWithSerializerAndState,
449             staticMap!(serializeAlgebraicAnnotationContinue!S, Filter!(serdeHasAlgebraicAnnotation, V.AllowedTypes)),
450             serializeAnnotatedValue,
451         )(serializer, value, wrapperState);
452     }
453     else
454     {
455         auto annotationsState = serializer.annotationsEnd(wrapperState);
456         static if (serdeGetAnnotationMembersOut!V.length)
457             serializeValueImpl(serializer, value);
458         else
459             serializeValue(serializer, value);
460         serializer.annotationWrapperEnd(annotationsState, wrapperState);
461     }
462 }
463 
464 /// Struct and class type serialization
465 private void serializeValueImpl(S, V)(scope ref S serializer, scope ref const V value)
466     if (isAggregateType!V && (!isIterable!V || hasFields!V || hasUDA!(V, serdeProxy) && !hasUDA!(V, serdeLikeList)))
467 {
468     import mir.algebraic;
469     auto state = serializer.structBegin;
470 
471     static if (hasUDA!(V, serdeDiscriminatedField))
472     {{
473         enum udas = getUDAs!(V, serdeDiscriminatedField);
474         static assert (udas.length == 1);
475         enum key = udas[0].field;
476 
477         static if (__traits(hasMember, S, "putCompiletimeKey"))
478         {
479             serializer.putCompiletimeKey!key;
480         }
481         else
482         static if (__traits(hasMember, S, "putKeyPtr"))
483         {
484             serializer.putKeyPtr(key.ptr);
485         }
486         else
487         {
488             serializer.putKey(key);
489         }
490 
491         serializer.putSymbol(udas[0].tag);
492     }}
493 
494     foreach(member; aliasSeqOf!(SerializableMembers!V))
495     {{
496         enum key = serdeGetKeyOut!(__traits(getMember, value, member));
497 
498         static if (key !is null)
499         {
500             static if (hasUDA!(__traits(getMember, value, member), serdeIgnoreDefault))
501             {
502                 if (__traits(getMember, value, member) == __traits(getMember, V.init, member))
503                     continue;
504             }
505 
506             static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreOutIf))
507             {
508                 alias pred = serdeGetIgnoreOutIf!(__traits(getMember, value, member));
509                 if (pred(__traits(getMember, value, member)))
510                     continue;
511             }
512 
513             static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreIfAggregate))
514             {
515                 alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member));
516                 if (pred(value))
517                     continue;
518             }
519 
520             static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreOutIfAggregate))
521             {
522                 alias pred = serdeGetIgnoreOutIfAggregate!(__traits(getMember, value, member));
523                 if (pred(value))
524                     continue;
525             }
526 
527             static if(__traits(hasMember, __traits(getMember, value, member), "serdeIgnoreOut"))
528             {
529                 if (__traits(getMember, __traits(getMember, value, member), "serdeIgnoreOut"))
530                     continue;
531             }
532 
533             static if(__traits(hasMember, __traits(getMember, value, member), "_void"))
534             {
535                 if (__traits(getMember, value, member)._is!void)
536                     continue;
537             }
538 
539             static if(hasUDA!(__traits(getMember, value, member), serdeTransformOut))
540             {
541                 alias f = serdeGetTransformOut!(__traits(getMember, value, member));
542                 auto val = f(__traits(getMember, value, member));
543                 alias W  = typeof(val);
544             }
545             else
546             static if (hasField!(V, member))
547             {
548                 scope valPtr = (()@trusted => &__traits(getMember, value, member))();
549                 alias W  = typeof(*valPtr);
550                 ref W val() @trusted pure @property { return *valPtr; }
551             }
552             else
553             {
554                 auto val = __traits(getMember, value, member);
555                 alias W  = typeof(val);
556             }
557 
558             static if (__traits(hasMember, S, "putCompiletimeKey"))
559             {
560                 serializer.putCompiletimeKey!key;
561             }
562             else
563             static if (__traits(hasMember, S, "putKeyPtr"))
564             {
565                 serializer.putKeyPtr((()@trusted => key.ptr)());
566             }
567             else
568             {
569                 serializer.putKey(key);
570             }
571 
572             static if(hasUDA!(__traits(getMember, value, member), serdeLikeList))
573             {
574                 static assert(0);
575             }
576             else
577             static if(hasUDA!(__traits(getMember, value, member), serdeLikeStruct))
578             {
579                 static if(is(W == interface) || is(W == class) || is(W : E[T], E, T))
580                 {
581                     if(val is null)
582                     {
583                         serializer.putNull(IonTypeCode.struct_);
584                         continue F;
585                     }
586                 }
587                 auto valState = serializer.beginStruct(val);
588                 static if (__traits(compiles, {foreach (keyElem; val.byKeyValue){}}))
589                 {
590                     foreach (keyElem; val.byKeyValue)
591                     {
592                         serializer.putKey(keyElem.key);
593                         serializer.serializeValue(keyElem.value);
594                     }
595                 }
596                 else
597                 {
598                     foreach (key, ref elem; val)
599                     {
600                         serializer.putKey(key);
601                         serializer.serializeValue(elem);
602                     }
603                 }
604                 serializer.structEnd(valState);
605             }
606             else
607             static if(hasUDA!(__traits(getMember, value, member), serdeProxy))
608             {
609                 serializeProxyCastImpl!(S, __traits(getMember, value, member))(serializer, val);
610             }
611             else
612             {
613                 serializer.serializeValue(val);
614             }
615         }
616     }}
617     static if(__traits(hasMember, V, "finalizeSerialization"))
618     {
619         value.finalizeSerialization(serializer);
620     }
621 
622     serializer.structEnd(state);
623 }
624 
625 private template serializeWithProxy(Proxy)
626 {
627     @safe
628     void serializeWithProxy(S, V)(scope ref S serializer, scope ref const V value)
629     {
630         static if (is(Proxy == const(char)[]) || is(Proxy == string) || is(Proxy == char[]))
631         {
632             import mir.format: stringBuf, print, getData;
633             static if (__traits(compiles, serializeValue(serializer, stringBuf() << value << getData)))
634                 serializeValue(serializer, stringBuf() << value << getData);
635             else
636             {
637                 serializeValue(serializer, to!Proxy(value));
638             }
639         }
640         else
641         {
642             static if (isImplicitlyConvertible!(const V, const Proxy))
643             {
644                 scope const Proxy proxy = value;
645                 serializeValue(serializer, proxy);
646             }
647             else
648             {
649                 static if (is(typeof(()@safe{return to!(const Proxy)(value);})))
650                     auto proxy = to!(const Proxy)(value);
651                 else
652                 {
653                     pragma(msg, "Mir warning: can't safely cast from ", (const V).stringof, " to ", (const Proxy).stringof
654                     );
655                     auto proxy = ()@trusted{return to!(const Proxy)(value);}();
656                 }
657                 serializeValue(serializer, proxy);
658             }
659         }
660     }
661 }
662 
663 private template serializeAlgebraicAnnotation(S)
664 {
665     @safe
666     void serializeAlgebraicAnnotation(V)(scope ref S serializer, scope ref const V value)
667         if (serdeHasAlgebraicAnnotation!V)
668     {
669         auto wrapperState = serializer.annotationWrapperBegin;
670         alias continueThis = serializeAlgebraicAnnotationContinue!S;
671         return continueThis(serializer, value, wrapperState);
672     }
673 }
674 
675 private template serializeAlgebraicAnnotationContinue(S)
676 {
677     void serializeAlgebraicAnnotationContinue(V)(scope ref S serializer, scope ref const V value, size_t wrapperState)
678         if (serdeHasAlgebraicAnnotation!V)
679     {
680         static if (__traits(hasMember, S, "putCompiletimeAnnotation"))
681             serializer.putCompiletimeAnnotation!(serdeGetAlgebraicAnnotation!V);
682         else
683         static if (__traits(hasMember, S, "putAnnotationPtr"))
684             serializer.putAnnotationPtr((()@trusted =>serdeGetAlgebraicAnnotation!V.ptr)());
685         else
686             serializer.putAnnotation(serdeGetAlgebraicAnnotation!V);
687         serializeAnnotatedValue(serializer, value, wrapperState);
688     }
689 }
690 
691 /// Struct and class type serialization
692 void serializeValue(S, V)(scope ref S serializer, scope ref const V value) @safe
693     if (isAggregateType!V)
694 {
695     import mir.algebraic: Algebraic, isVariant, isNullable;
696     import mir.string_map: isStringMap;
697     import mir.timestamp: Timestamp;
698 
699     static if(is(V == class) || is(V == interface))
700     {
701         if(value is null)
702         {
703             serializer.putNull(nullTypeCodeOf!V);
704             return;
705         }
706     }
707 
708     static if (isBigInt!V || isDecimal!V || isTimestamp!V || isBlob!V || isClob!V)
709     {
710         serializer.putValue(value);
711         return;
712     }
713     else
714     static if (isTuple!V)
715     {
716         auto state = serializer.listBegin(value.expand.length);
717         foreach (ref v; value.expand)
718         {
719             serializer.elemBegin;
720             .serializeValue(serializer, v);
721         }
722         serializer.listEnd(state);
723     }
724     else
725     static if (isStringMap!V)
726     {
727         auto state = serializer.beginStruct(value);
728         auto keys = value.keys;
729         foreach (i, ref v; value.values)
730         {
731             serializer.putKey(keys[i]);
732             .serializeValue(serializer, v);
733         }
734         serializer.structEnd(state);
735     }
736     else
737     static if (serdeGetAnnotationMembersOut!V.length || isAnnotated!V)
738     {
739         auto wrapperState = serializer.annotationWrapperBegin;
740         return serializeAnnotatedValue(serializer, value, wrapperState);
741     }
742     else
743     static if ((isIterable!V || isRefIterable!V) &&
744         (!hasProxy!V || hasLikeList!V) &&
745         !hasFields!V &&
746         !isStdNullable!V)
747     {
748         static if(is(V : E[], E) && !is(V : D[N], D, size_t N))
749         {
750             if (value is null)
751             {
752                 serializer.putNull(nullTypeCodeOf!V);
753                 return;
754             }
755         }
756         static if (isSomeChar!(ForeachType!V))
757         {
758             import mir.format: stringBuf;
759             auto buf = stringBuf;
760             static if (isIterable!(const V))
761             {
762                 static if (is(typeof(()@safe{foreach (elem; value){}})))
763                     foreach (elem; value) buf.put(elem);
764                 else () @trusted
765                 {
766                     pragma(msg, "Mir warning: can't @safely iterate ", (const V).stringof);
767                     foreach (elem; value) buf.put(elem);
768                 }();
769             }
770             else
771             {
772                 pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof);
773                 static if (!is(typeof(()@safe{foreach (elem; (()@trusted => cast() value)()){}})))
774                     pragma(msg, "Mir warning: can't @safely iterate ", (V).stringof);
775                 () @trusted
776                 {
777                     foreach (elem; cast() value) buf.put(elem);
778                 }();
779             }
780             serializer.putValue(buf.data);
781         }
782         else
783         {
784             auto state = serializer.beginList(value);
785             static if (isRefIterable!V)
786             {
787                 static if (__traits(hasMember, V, "lightScope"))
788                 {
789                     foreach (ref elem; value.lightScope)
790                     {
791                         serializer.elemBegin;
792                         serializer.serializeValue(elem);
793                     }
794                 }
795                 else
796                 static if (isRefIterable!(const V))
797                 {
798                     static if (is(typeof(()@safe{foreach (ref elem; value){}})))
799                     foreach (ref elem; value)
800                     {
801                         serializer.elemBegin;
802                         serializer.serializeValue(elem);
803                     }
804                     else ()@trusted
805                     {
806                         pragma(msg, "Mir warning: can't @safely iterate ", (const V).stringof);
807                     foreach (ref elem; value)
808                     {
809                         serializer.elemBegin;
810                         serializer.serializeValue(elem);
811                     }
812                     } ();
813                 }
814                 else
815                 {
816                     pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof);
817                     foreach (ref elem; (()@trusted => cast() value)())
818                     {
819                         serializer.elemBegin;
820                         serializer.serializeValue(elem);
821                     }
822                 }
823             }
824             else
825             {
826                 static if (__traits(hasMember, V, "lightScope"))
827                 {
828                     foreach (elem; value.lightScope)
829                     {
830                         serializer.elemBegin;
831                         serializer.serializeValue(elem);
832                     }
833                 }
834                 else
835                 static if (isIterable!(const V))
836                 {
837                     foreach (elem; value)
838                     {
839                         serializer.elemBegin;
840                         serializer.serializeValue(elem);
841                     }
842                 }
843                 else
844                 {
845                     pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof);
846                     static if (is(typeof(()@safe{foreach (elem; (()@trusted => cast() value)()){}})))
847                     foreach (elem; (()@trusted => cast() value)())
848                     {
849                         serializer.elemBegin;
850                         serializer.serializeValue(elem);
851                     }
852                     else ()@trusted
853                     {
854                         pragma(msg, "Mir warning: can't @safely iterate ", (V).stringof);
855                     foreach (elem; cast() value)
856                     {
857                         serializer.elemBegin;
858                         serializer.serializeValue(elem);
859                     }
860                     } ();
861                 }
862             }
863             serializer.listEnd(state);
864         }
865     }
866     else
867     static if(hasUDA!(V, serdeLikeList))
868     {
869         static assert(0);
870     }
871     else
872     static if(hasUDA!(V, serdeLikeStruct))
873     {
874         static if (is(V : E[T], E, T))
875         {
876             if (value is null)
877             {
878                 serializer.putNull(nullTypeCodeOf!V);
879                 continue F;
880             }
881         }
882         auto valState = serializer.beginStruct(value);
883 
884         import mir.algebraic: isVariant, visit;
885         static if (__traits(hasMember, value, "byKeyValue"))
886         {
887             static if (is(typeof(()@safe {foreach (keyElem; value.byKeyValue){}})))
888             foreach (keyElem; value.byKeyValue)
889             {
890                 static if (!isVariant!(typeof(keyElem.key)))
891                     serializer.putKey(keyElem.key);
892                 else
893                 {
894                     if (keyElem.key._is!string)
895                         serializer.putKey(keyElem.key.trustedGet!string);
896                     else
897                     if (keyElem.key._is!long)
898                         serializer.putKey(keyElem.key.trustedGet!long.to!string);
899                     else
900                     if (keyElem.key._is!double)
901                         serializer.putKey(keyElem.key.trustedGet!double.to!string);
902                     else
903                     if (keyElem.key._is!Timestamp)
904                         serializer.putKey(keyElem.key.trustedGet!Timestamp.to!string);
905                 }
906                 serializer.serializeValue(keyElem.value);
907             }
908             else
909             {
910                 pragma(msg, "Mir warning: can't safely iterate ", typeof(value));
911                 () @trusted {
912             foreach (keyElem; (cast() value).byKeyValue)
913             {
914                 static if (!isVariant!(typeof(keyElem.key)))
915                     serializer.putKey(keyElem.key);
916                 else
917                 {
918                     if (keyElem.key._is!string)
919                         serializer.putKey(keyElem.key.trustedGet!string);
920                     else
921                     if (keyElem.key._is!long)
922                         serializer.putKey(keyElem.key.trustedGet!long.to!string);
923                     else
924                     if (keyElem.key._is!double)
925                         serializer.putKey(keyElem.key.trustedGet!double.to!string);
926                     else
927                     if (keyElem.key._is!Timestamp)
928                         serializer.putKey(keyElem.key.trustedGet!Timestamp.to!string);
929                 }
930                 serializer.serializeValue(keyElem.value);
931             }
932             }();}
933         }
934         else
935         {
936             foreach (key, ref elem; value)
937             {
938                 static if (!isVariant!(typeof(key)))
939                     serializer.putKey(key);
940                 else
941                 {
942                     if (key._is!string)
943                         serializer.putKey(key.trustedGet!string);
944                     else
945                     if (key._is!long)
946                         serializer.putKey(key.trustedGet!long.to!string);
947                     else
948                     if (key._is!double)
949                         serializer.putKey(key.trustedGet!double.to!string);
950                     else
951                     if (key._is!Timestamp)
952                         serializer.putKey(key.trustedGet!Timestamp.to!string);
953                 }
954                 serializer.serializeValue(elem);
955             }
956         }
957         serializer.structEnd(valState);
958     }
959     else
960     static if (isAlgebraicAliasThis!V)
961     {
962         serializeValue(serializer, __traits(getMember, value, __traits(getAliasThis, V)));
963     }
964     else
965     static if (is(V == Algebraic!TypeSet, TypeSet...) && (!isStdNullable!V || Algebraic!TypeSet.AllowedTypes.length != 2))
966     {
967         import mir.algebraic: match;
968         match!(
969             serializeVoidHandlerWithSerializer,
970             staticMap!(serializeAlgebraicAnnotation!S, Filter!(serdeHasAlgebraicAnnotation, V.AllowedTypes)),
971             serializeValue,
972         )(serializer, value);
973     }
974     else
975     static if(staticIndexOf!("serialize", __traits(allMembers, V)) >= 0)
976     {
977         alias soverloads = getSerializeOverloads!(S, V);
978         static if (__traits(hasMember, soverloads, "best") || !__traits(hasMember, soverloads, "script"))
979         {
980             static if (__traits(compiles, value.serialize(serializer)) || !hasUDA!(V, serdeProxy))
981                 value.serialize(serializer);
982             else
983                 serializeValue(serializer, to!(serdeGetProxy!V)(value));
984 
985         }
986         else
987         static if (__traits(hasMember, soverloads, "script"))
988         {
989             import mir.ser.interfaces: SerializerWrapper;
990             scope wserializer = new SerializerWrapper!S(serializer);
991             auto iserializer = wserializer.ISerializer;
992             value.serialize(iserializer);
993         }
994         return;
995     }
996     else
997     static if (hasUDA!(V, serdeProxy))
998     {
999         serializeProxyCastImpl!(S, V)(serializer, value);
1000         return;
1001     }
1002     else
1003     static if (isStdNullable!V || isNullable!V)
1004     {
1005         if(value.isNull)
1006         {
1007             serializer.putNull(nullTypeCodeOf!(typeof(V.init.get())));
1008             return;
1009         }
1010         return serializeValue(serializer, value.get);
1011     }
1012     else
1013     static if (is(typeof(Timestamp(V.init))))
1014     {
1015         serializer.putValue(Timestamp(value));
1016         return;
1017     }
1018     else
1019     {
1020         return serializeValueImpl(serializer, value);
1021     }
1022 }
1023 
1024 private template getSerializeOverloads(S, alias value)
1025 {
1026     import mir.ser.interfaces: ISerializer;
1027     static foreach (i, so; __traits(getOverloads, value, "serialize"))
1028     {
1029         static if (!__traits(isTemplate, value.serialize))
1030         {
1031             static if (is(Parameters!so[0] == S))
1032             {
1033                 enum best = i;
1034             }
1035             else
1036             {
1037                 static if (is(Parameters!so[0] == ISerializer))
1038                 {
1039                     enum script = i;
1040                 }
1041             }
1042         }
1043     }
1044 }
1045 
1046 private struct UntrustDummy
1047 {
1048     long[] a;
1049     double b;
1050     bool c;
1051     string[] d;
1052 }
1053 
1054 import std.traits: isFunctionPointer, isDelegate;
1055 
1056 auto assumePure(T)(T t) @trusted
1057 if (isFunctionPointer!T || isDelegate!T)
1058 {
1059     pragma(inline, false);
1060     enum attrs = functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
1061     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
1062 }
1063 
1064 
1065 /// Mir types
1066 version(mir_ion_test)
1067 unittest
1068 {
1069     import mir.bignum.integer;
1070     import mir.date;
1071     import mir.ser.json: serializeJson;
1072     import mir.ser.text: serializeText;
1073     assert(Date(2021, 4, 24).serializeJson == `"2021-04-24"`);
1074     assert(BigInt!2(123).serializeJson == `123`);
1075     assert(Date(2021, 4, 24).serializeText == `2021-04-24`);
1076     assert(BigInt!2(123).serializeText == `123`);
1077 }
1078 
1079 /// Alias this support
1080 version(mir_ion_test)
1081 unittest
1082 {
1083     struct S
1084     {
1085         int u;
1086     }
1087 
1088     struct C
1089     {
1090         int b;
1091         S s;
1092         alias s this; 
1093     }
1094 
1095     import mir.ser.json: serializeJson;
1096     assert(C(4, S(3)).serializeJson == `{"u":3,"b":4}`);
1097 }
1098 
1099 /// Custom `serialize`
1100 version(mir_ion_test)
1101 unittest
1102 {
1103     struct S
1104     {
1105         void serialize(S)(scope ref S serializer) scope const @safe
1106         {
1107             auto state = serializer.structBegin(1);
1108             serializer.putKey("foo");
1109             serializer.putValue("bar");
1110             serializer.structEnd(state);
1111         }
1112     }
1113 
1114     import mir.ser.json: serializeJson;
1115     assert(serializeJson(S()) == `{"foo":"bar"}`);
1116 }
1117 
1118 /// Nullable type serialization
1119 version(mir_ion_test)
1120 unittest
1121 {
1122     import mir.ser.json: serializeJson;
1123     import mir.algebraic: Nullable;
1124 
1125     struct Nested
1126     {
1127         float f;
1128     }
1129 
1130     struct T
1131     {
1132         string str;
1133         Nullable!Nested nested;
1134     }
1135 
1136     T t;
1137     assert(t.serializeJson == `{"str":"","nested":{}}`);
1138     t.str = "txt";
1139     t.nested = Nested(123);
1140     assert(t.serializeJson == `{"str":"txt","nested":{"f":123.0}}`);
1141 }
1142 
1143 ///
1144 version(mir_ion_test)
1145 unittest
1146 {
1147     import mir.algebraic;
1148     import mir.small_string;
1149 
1150     @serdeAlgebraicAnnotation("$B")
1151     static struct B
1152     {
1153         double number;
1154     }
1155 
1156     @serdeAlgebraicAnnotation("$A")
1157     static struct A
1158     {
1159         @serdeAnnotation
1160         SmallString!32 id1;
1161 
1162         @serdeAnnotation
1163         string[] id2;
1164 
1165         B c;
1166         alias c this;
1167 
1168         string s;
1169     }
1170 
1171 
1172     static assert(serdeHasAlgebraicAnnotation!B);
1173     static assert(serdeGetAlgebraicAnnotation!B == "$B");
1174     static assert(serdeHasAlgebraicAnnotation!A);
1175     static assert(serdeGetAlgebraicAnnotation!A == "$A");
1176 
1177     @serdeAlgebraicAnnotation("$c")
1178     static struct C
1179     {
1180     }
1181 
1182     @serdeAlgebraicAnnotation("$E")
1183     enum E { e1, e2 }
1184 
1185     @serdeAlgebraicAnnotation("$S")
1186     static struct S
1187     {
1188         @serdeAnnotation
1189         string sid;
1190 
1191         @serdeAnnotation
1192         string sid2;
1193 
1194         alias Data = Nullable!(A, C, long, E);
1195 
1196         alias data this;
1197 
1198         Data data;
1199     }
1200 
1201 
1202     @serdeAlgebraicAnnotation("$Y")
1203     static struct Y
1204     {
1205         alias Data = Nullable!(A, C, long);
1206 
1207         alias data this;
1208 
1209         Data data;
1210     }
1211 
1212 
1213     import mir.ion.conv: ion2text;
1214     import mir.ser.ion: serializeIon;
1215     import mir.ser.text: serializeText;
1216     import mir.test;
1217 
1218     () {
1219         Nullable!S value = S("LIBOR", "S", S.Data(A("Rate".SmallString!32, ["USD", "GBP"])));
1220         static immutable text = `LIBOR::S::$A::Rate::USD::GBP::{number:nan,s:null.string}`;
1221         value.serializeText.should == text;
1222         auto binary = value.serializeIon;
1223         binary.ion2text.should == text;
1224         import mir.deser.ion: deserializeIon;
1225         binary.deserializeIon!S.serializeText.should == text;
1226     } ();
1227 
1228     () {
1229         S value = S("LIBOR", "S", S.Data(E.e2));
1230         static immutable text = `LIBOR::S::$E::e2`;
1231         value.serializeText.should == text;
1232         auto binary = value.serializeIon;
1233         binary.ion2text.should == text;
1234         import mir.deser.ion: deserializeIon;
1235         binary.deserializeIon!S.serializeText.should == text;
1236     } ();
1237 
1238     () {
1239         auto value = Y(Y.Data(A("Rate".SmallString!32, ["USD", "GBP"])));
1240         static immutable text = `$A::Rate::USD::GBP::{number:nan,s:null.string}`;
1241         auto binary = value.serializeIon;
1242         binary.ion2text.should == text;
1243         import mir.deser.ion: deserializeIon;
1244         binary.deserializeIon!Y.serializeText.should == text;
1245     } ();
1246 
1247     () {
1248         auto value = S("USD", "S", S.Data(3));
1249         static immutable text = `USD::S::3`;
1250         auto binary = value.serializeIon;
1251         binary.ion2text.should == text;
1252         import mir.deser.ion: deserializeIon;
1253         binary.deserializeIon!S.serializeText.should == text;
1254     } ();
1255 }
1256 
1257 /++
1258 +/
1259 auto beginList(S, V)(scope ref S serializer, scope ref V value)
1260 {
1261     static if (__traits(compiles, serializer.listBegin))
1262     {
1263         return serializer.listBegin;
1264     }
1265     else
1266     {
1267         import mir.primitives: walkLength;
1268         return serializer.listBegin(value.walkLength);
1269     }
1270 }
1271 
1272 /++
1273 +/
1274 auto beginSexp(S, V)(scope ref S serializer, scope ref V value)
1275 {
1276     static if (__traits(compiles, serializer.sexpBegin))
1277     {
1278         return serializer.sexpBegin;
1279     }
1280     else
1281     {
1282         import mir.primitives: walkLength;
1283         return serializer.sexpBegin(value.walkLength);
1284     }
1285 }
1286 
1287 /++
1288 +/
1289 auto beginStruct(S, V)(scope ref S serializer, scope ref V value)
1290 {
1291     static if (__traits(compiles, serializer.structBegin))
1292     {
1293         return serializer.structBegin;
1294     }
1295     else
1296     {
1297         import mir.primitives: walkLength;
1298         return serializer.structBegin(value.walkLength);
1299     }
1300 }
1301 
1302 version (Have_mir_bloomberg)
1303 {
1304     import mir.ser.bloomberg : BloombergElement;
1305     ///
1306     void serializeValue(S)(scope ref S serializer, scope const(BloombergElement)* value)
1307     {
1308         import mir.ser.bloomberg : impl = serializeValue;
1309         return impl(serializer, value);
1310     }
1311 }