1 /++
2 DRAFT
3 Macros:
4     AlgorithmREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, $1)$(NBSP)
5     AAREF = $(REF_ALTTEXT $(TT $2), $2, mir, algebraic_alias, $1)$(NBSP)
6 +/
7 module mir.ion.examples;
8 
9 /// A user may define setter and/or getter properties.
10 @safe pure
11 version(mir_ion_test) unittest
12 {
13     import mir.ser.json;
14     import mir.deser.json;
15     import mir.conv: to;
16 
17     static struct S
18     {
19         @serdeIgnore string str;
20     @safe pure:
21         string a() const @property
22         {
23             return str;
24         }
25 
26         void b(int s) @property
27         {
28             str = s.to!string;
29         }
30     }
31 
32     assert(S("str").serializeJson == `{"a":"str"}`);
33     assert(`{"b":123}`.deserializeJson!S.str == "123");
34 }
35 
36 /// Support for custom nullable types (types that has a bool property `isNull`,
37 /// non-void property `get` returning payload and void property `nullify` that
38 /// makes nullable type to null value)
39 @safe pure
40 version(mir_ion_test) unittest
41 {
42     import mir.ser.json;
43     import mir.deser.json;
44 
45     static struct MyNullable
46     {
47         long value;
48 
49     @safe pure nothrow @nogc:
50 
51         @property
52         isNull() const
53         {
54             return value == 0;
55         }
56 
57         @property
58         long get() const
59         {
60             return value;
61         }
62 
63         @property
64         nullify()
65         {
66             value = 0;
67         }
68 
69         auto opAssign(long value)
70         {
71             this.value = value;
72         }
73     }
74 
75     static struct Foo
76     {
77         MyNullable my_nullable;
78         string field;
79 
80         bool opEquals()(auto ref const(typeof(this)) rhs) const
81         {
82             if (my_nullable.isNull && rhs.my_nullable.isNull)
83                 return field == rhs.field;
84 
85             if (my_nullable.isNull != rhs.my_nullable.isNull)
86                 return false;
87 
88             return my_nullable == rhs.my_nullable &&
89                          field == rhs.field;
90         }
91     }
92 
93     Foo foo;
94     foo.field = "it's a foo";
95 
96     assert (serializeJson(foo) == `{"my_nullable":null,"field":"it's a foo"}`);
97 
98     foo.my_nullable = 200;
99 
100     assert (deserializeJson!Foo(`{"my_nullable":200,"field":"it's a foo"}`) == Foo(MyNullable(200), "it's a foo"));
101 
102     import mir.algebraic: Nullable;
103 
104     static struct Bar
105     {
106         Nullable!long nullable;
107         string field;
108 
109         bool opEquals()(auto ref const(typeof(this)) rhs)
110         {
111             if (nullable.isNull && rhs.nullable.isNull)
112                 return field == rhs.field;
113 
114             if (nullable.isNull != rhs.nullable.isNull)
115                 return false;
116 
117             return nullable == rhs.nullable &&
118                          field == rhs.field;
119         }
120     }
121 
122     Bar bar;
123     bar.field = "it's a bar";
124 
125     assert (serializeJson(bar) == `{"nullable":null,"field":"it's a bar"}`);
126 
127     bar.nullable = 777;
128     assert (deserializeJson!Bar(`{"nullable":777,"field":"it's a bar"}`) == Bar(Nullable!long(777), "it's a bar"));
129 }
130 
131 ///
132 @safe pure
133 version(mir_ion_test) unittest
134 {
135     import mir.ser.ion;
136     import mir.ser.json;
137     import mir.ion.stream;
138 
139     IonValueStream[string] map;
140 
141     map["num"] = IonValueStream(serializeIon(124));
142     map["str"] = IonValueStream(serializeIon("value"));
143     
144     auto json = map.serializeJson;
145     assert(json == `{"str":"value","num":124}` || json == `{"num":124,"str":"value"}`);
146 }
147 
148 /// Support for floating point nan and (partial) infinity
149 version(mir_ion_test) unittest
150 {
151     import mir.conv: to;
152     import mir.ser.ion;
153     import mir.ser.json;
154     import mir.deser.json;
155     import mir.ion.conv;
156 
157     static struct Foo
158     {
159         float f;
160 
161         bool opEquals()(auto ref const(typeof(this)) rhs)
162         {
163             return  f != f && rhs.f != rhs.f || f == rhs.f;
164         }
165     }
166 
167     // test for Not a Number
168     assert (serializeJson(Foo()) == `{"f":"nan"}`, serializeJson(Foo()));
169     assert (serializeIon(Foo()).ion2json == `{"f":"nan"}`, serializeIon(Foo()).ion2json);
170 
171     assert (deserializeJson!Foo(`{"f":"nan"}`) == Foo(), deserializeJson!Foo(`{"f":"nan"}`).to!string);
172 
173     assert (serializeJson(Foo(1f/0f)) == `{"f":"+inf"}`);
174     assert (serializeIon(Foo(1f/0f)).ion2json == `{"f":"+inf"}`);
175     assert (deserializeJson!Foo(`{"f":"+inf"}`)  == Foo( float.infinity));
176     assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity));
177 
178     assert (serializeJson(Foo(-1f/0f)) == `{"f":"-inf"}`);
179     assert (serializeIon(Foo(-1f/0f)).ion2json == `{"f":"-inf"}`);
180     assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity));
181 }
182 
183 ///
184 version(mir_ion_test) unittest
185 {
186     import mir.ser.ion;
187     import mir.ser.json;
188     import mir.deser.json;
189     import mir.ion.conv;
190 
191     static struct S
192     {
193         string foo;
194         uint bar;
195     }
196 
197     static immutable json = `{"foo":"str","bar":4}`;
198     assert(serializeIon(S("str", 4)).ion2json == json);
199     assert(serializeJson(S("str", 4)) == json);
200     assert(deserializeJson!S(json) == S("str", 4));
201 }
202 
203 /// Proxy for members
204 version(mir_ion_test) unittest
205 {
206     import mir.ser.json;
207     import mir.deser.json;
208 
209     struct S
210     {
211         // const(char)[] doesn't reallocate ASDF data.
212         @serdeProxy!(const(char)[])
213         uint bar;
214     }
215 
216     auto json = `{"bar":"4"}`;
217     assert(serializeJson(S(4)) == json);
218     assert(deserializeJson!S(json) == S(4));
219 }
220 
221 ///
222 pure version(mir_ion_test) unittest
223 {
224     import mir.ser.json;
225     import mir.deser.json;
226     import mir.serde: serdeKeys;
227     static struct S
228     {
229         @serdeKeys("b", "a")
230         string s;
231     }
232     assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"b":"d"}`);
233 }
234 
235 ///
236 pure version(mir_ion_test) unittest
237 {
238     import mir.ser.json;
239     import mir.deser.json;
240     import mir.serde: serdeKeys, serdeKeyOut;
241     static struct S
242     {
243         @serdeKeys("a")
244         @serdeKeyOut("s")
245         string s;
246     }
247     assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"s":"d"}`);
248 }
249 
250 ///
251 pure version(mir_ion_test) unittest
252 {
253     import mir.ser.json;
254     import mir.deser.json;
255     import std.exception: assertThrown;
256 
257     struct S
258     {
259         string field;
260     }
261     
262     assert(`{"field":"val"}`.deserializeJson!S.field == "val");
263     // assertThrown(`{"other":"val"}`.deserializeJson!S);
264 }
265 
266 ///
267 version(mir_ion_test) unittest
268 {
269     import mir.ser.json;
270     import mir.deser.json;
271 
272     static struct S
273     {
274         @serdeKeyOut("a")
275         string s;
276     }
277     assert(`{"s":"d"}`.deserializeJson!S.serializeJson == `{"a":"d"}`);
278 }
279 
280 ///
281 version(mir_ion_test) unittest
282 {
283     import mir.ser.json;
284     import mir.deser.json;
285     import std.exception: assertThrown;
286 
287     static struct S
288     {
289         @serdeIgnore
290         string s;
291     }
292     // assertThrown(`{"s":"d"}`.deserializeJson!S);
293     assert(S("d").serializeJson == `{}`);
294 }
295 
296 ///
297 version(mir_ion_test) unittest
298 {
299     import mir.ser.json;
300     import mir.deser.json;
301 
302     static struct Decor
303     {
304         int candles; // 0
305         float fluff = float.infinity; // inf 
306     }
307     
308     static struct Cake
309     {
310         @serdeIgnoreDefault string name = "Chocolate Cake";
311         @serdeIgnoreDefault string nullStr;
312         @serdeIgnoreDefault string emptyStr = "";
313         int slices = 8;
314         float flavor = 1;
315         @serdeIgnoreDefault
316         Decor dec = Decor(20); // { 20, inf }
317     }
318     
319     assert(Cake("Normal Cake", "", null).serializeJson == `{"name":"Normal Cake","slices":8,"flavor":1.0}`);
320     auto cake = Cake.init;
321     cake.dec = Decor.init;
322     assert(cake.serializeJson == `{"slices":8,"flavor":1.0,"dec":{"candles":0,"fluff":"+inf"}}`);
323     assert(cake.dec.serializeJson == `{"candles":0,"fluff":"+inf"}`);
324     
325     static struct A
326     {
327         @serdeIgnoreDefault
328         string str = "Banana";
329         int i = 1;
330     }
331     assert(A.init.serializeJson == `{"i":1}`);
332     
333     static struct S
334     {
335         @serdeIgnoreDefault
336         A a;
337     }
338     assert(S.init.serializeJson == `{}`);
339     assert(S(A("Berry")).serializeJson == `{"a":{"str":"Berry","i":1}}`);
340     
341     static struct D
342     {
343         S s;
344     }
345     assert(D.init.serializeJson == `{"s":{}}`);
346     assert(D(S(A("Berry"))).serializeJson == `{"s":{"a":{"str":"Berry","i":1}}}`);
347     assert(D(S(A(null, 0))).serializeJson == `{"s":{"a":{"str":"","i":0}}}`);
348     
349     static struct F
350     {
351         D d;
352     }
353     assert(F.init.serializeJson == `{"d":{"s":{}}}`);
354 }
355 
356 ///
357 version(mir_ion_test) unittest
358 {
359     import mir.ser.json;
360     import mir.deser.json;
361     import mir.serde: serdeIgnoreOut;
362 
363     static struct S
364     {
365         @serdeIgnoreOut
366         string s;
367     }
368     assert(`{"s":"d"}`.deserializeJson!S.s == "d");
369     assert(S("d").serializeJson == `{}`);
370 }
371 
372 ///
373 version(mir_ion_test) unittest
374 {
375     import mir.ser.json;
376     import mir.deser.json;
377 
378     static struct CustomType
379     {
380         int a;
381 
382         // can be part of a type as well (only omits on property keys)
383         auto serdeIgnoreOut() const
384         {
385             return a < 0;
386         }
387 
388         // and use custom serializer to serialize as int
389         void serialize(S)(scope ref S serializer) scope const
390         {
391             import mir.ser : serializeValue;
392 
393             serializeValue(serializer, a);
394         }
395     }
396 
397     static struct S
398     {
399         @serdeIgnoreOutIf!`a < 0`
400         int a;
401 
402         // b is not output if the serdeIgnoreOut property in its type evaluates to false.
403         CustomType b;
404     }
405 
406     assert(serializeJson(S(3, CustomType(2))) == `{"a":3,"b":2}`, serializeJson(S(3, CustomType(2))));
407     assert(serializeJson(S(-3, CustomType(-2))) == `{}`);
408 }
409 
410 ///
411 @safe pure
412 version(mir_ion_test) unittest
413 {
414     import mir.ser.json;
415     import mir.deser.json;
416 
417     import std.uuid: UUID;
418 
419     static struct S
420     {
421         @serdeScoped
422         @serdeProxy!string
423         UUID id;
424     }
425 
426     enum result = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46");
427     assert(`{"id":"8AB3060E-2cba-4f23-b74c-b52db3bdfb46"}`.deserializeJson!S.id == result);
428 }
429 
430 ///
431 version(mir_ion_test) unittest
432 {
433     import mir.ser.json;
434     import mir.deser.json;
435     import mir.ion.value;
436     import mir.algebraic: Variant;
437 
438     static struct ObjectA
439     {
440         string name;
441     }
442     static struct ObjectB
443     {
444         double value;
445     }
446 
447     alias MyObject = Variant!(ObjectA, ObjectB);
448 
449     static struct MyObjectArrayProxy
450     {
451         MyObject[] array;
452 
453         this(inout(MyObject)[] array) @safe pure nothrow @nogc inout
454         {
455             this.array = array;
456         }
457 
458         inout(T) opCast(T : MyObject[])() inout
459         {
460             return array;
461         }
462 
463         void serialize(S)(scope ref S serializer) scope const
464         {
465             import mir.ser: serializeValue;
466             // mir.algebraic has builtin support for serialization.
467             // For other algebraic libraies one can use thier visitor handlers.
468             serializeValue(serializer, array);
469         }
470 
471         /++
472         Returns: error msg if any
473         +/
474         @trusted pure scope
475         IonException deserializeFromIon(scope const char[][] symbolTable, scope IonDescribedValue value)
476         {
477             import mir.deser.ion: deserializeIon;
478             import mir.ion.exception;
479             foreach (IonErrorCode error, scope IonDescribedValue elem; value.get!IonList)
480             {
481                 if (error)
482                     return error.ionException;
483                 array ~= "name" in elem.get!IonStruct.withSymbols(symbolTable)
484                     ? MyObject(deserializeIon!ObjectA(symbolTable, elem))
485                     : MyObject(deserializeIon!ObjectB(symbolTable, elem));
486             }
487             return null;
488         }
489     }
490 
491     static struct SomeObject
492     {
493         @serdeProxy!MyObjectArrayProxy MyObject[] objects;
494     }
495 
496     string data = q{{"objects":[{"name":"test"},{"value":1.5}]}};
497 
498     auto value = data.deserializeJson!SomeObject;
499     assert (value.serializeJson == data, value.serializeJson);
500 }
501 
502 // TODO
503 // version(none)
504 // unittest
505 // {
506 //     Asdf[string] map;
507 
508 //     map["num"] = serializeToAsdf(124);
509 //     map["str"] = serializeToAsdf("value");
510 
511 //     import std.stdio;
512 //     map.serializeToJson.writeln();
513 // }
514 
515 // TODO
516 // version(none)
517 // unittest
518 // {
519 //     import mir.algebraic: Variant, Nullable, This;
520 //     alias V = Nullable!(double, string, This[], This[string]);
521 //     V v;
522 //     assert(v.serializeToJson == "null", v.serializeToJson);
523 //     v = [V(2), V("str"), V(["key":V(1.0)])];
524 //     assert(v.serializeToJson == `[2.0,"str",{"key":1.0}]`);
525 // }
526 
527 /// reflectSerde support
528 version(mir_ion_test) unittest
529 {
530     import mir.reflection: reflectSerde;
531     static struct S
532     {
533         @reflectSerde enum s = "str";
534     }
535 
536     import mir.ser.text: serializeText;
537     assert(S().serializeText == `{s:"str"}`);
538 }
539 
540 /// serdeIgnoreUnexpectedKeys support
541 version(mir_ion_test) unittest
542 {
543     import mir.serde: serdeIgnoreUnexpectedKeys;
544     @serdeIgnoreUnexpectedKeys
545     static struct S { int i; }
546 
547     import mir.deser.text: deserializeText;
548     assert(`{s:"str",i:4}`.deserializeText!S == S(4));
549 }
550 
551 /// Iterable and serdeLikeList
552 version(mir_ion_test) unittest
553 {
554     import mir.deser.text: deserializeText;
555     import mir.ser.text: serializeText;
556     import mir.serde: serdeIgnoreOut, serdeLikeList, serdeProxy, serdeKeys;
557     import std.array: Appender;
558 
559     static struct S
560     {
561         @serdeLikeList
562         @serdeProxy!string // input element type of
563         @serdeIgnoreOut
564         @serdeKeys("strings")
565         Appender!(string[]) stringsApp; //`put` method is used
566 
567         this(const(string)[] strings)
568         {
569             stringsApp.put(strings);
570         }
571 
572         auto strings() const @property // Iterable
573         {
574             import mir.ndslice.topology: map;
575             return stringsApp.data.map!((string s) => "_" ~ s);
576         }
577 
578     }
579 
580     assert(S([`a`, `b`]).serializeText == `{strings:["_a","_b"]}`);
581     import mir.test;
582     `{strings:["a","b"]}`.deserializeText!S.stringsApp.data.should == [`a`, `b`];
583 
584     @serdeLikeList
585     @serdeProxy!string // input element type of
586     static struct C
587     {
588         /// Used to make S deserializable
589         Appender!(string[]) stringsApp; //`put` method is used
590         alias stringsApp this;
591 
592         this(const(string)[] strings)
593         {
594             stringsApp.put(strings);
595         }
596 
597         /// To make C iterable and serializable
598         auto opIndex() const { return stringsApp.data; }
599     }
600 
601     static struct S2 {
602         static prop() @property { return "100"; };
603     }
604 
605     assert(S2().serializeText == `{prop:"100"}`, S2().serializeText);
606     assert(C([`a`, `b`]).serializeText == `["a","b"]`);
607     assert(`["a","b"]`.deserializeText!C.stringsApp.data == [`a`, `b`]);
608 }
609 
610 /// serdeLikeStruct
611 unittest
612 {
613     import mir.test: should;
614     import mir.ser.text: serializeText;
615     import mir.deser.text: deserializeText;
616     import mir.serde: serdeLikeStruct, serdeProxy;
617 
618     // opApply for serialization & and opIndexAssign for deserialization
619     static struct M
620     {
621         private int sum;
622 
623         // opApply is used for serialization
624         int opApply(scope int delegate(scope const char[] key, ref const int val) pure @safe dg) const pure @safe
625         {
626             { int var = 1; if (auto r = dg("a", var)) return r; }
627             { int var = 2; if (auto r = dg("b", var)) return r; }
628             { int var = 3; if (auto r = dg("c", var)) return r; }
629             return 0;
630         }
631 
632         // opIndexAssign for deserialization
633         void opIndexAssign(int val, string key) pure @safe
634         {
635             sum += val;
636         }
637     }
638 
639     // attribute on member + opApply
640     static struct S
641     {
642         @serdeLikeStruct
643         @serdeProxy!int // for deserialization
644         M obj;
645     }
646 
647     assert(S().serializeText == `{obj:{a:1,b:2,c:3}}`);
648     `{obj:{a:1,b:2,c:3}}`.deserializeText!S.obj.sum.should == 6;
649 
650     // attribute on type + opApply
651     @serdeLikeStruct
652     @serdeProxy!int // for deserialization
653     static struct C
654     {
655         M obj;
656         alias obj this;
657     }
658 
659     assert(C().serializeText == `{a:1,b:2,c:3}`);
660     assert(`{a:1,b:2,c:3}`.deserializeText!C.obj.sum == 6);
661 
662     // byKeyValue
663     static struct D
664     {
665         static struct KeyValue
666         {
667             string key;
668             int value;
669         }
670         KeyValue[] byKeyValue = [KeyValue("a", 1), KeyValue("b", 2), KeyValue("c", 3)];
671     }
672 
673     // attribute on member + byKeyValue
674     static struct F
675     {
676         @serdeLikeStruct
677         D obj;
678     }
679 
680     assert(F().serializeText == `{obj:{a:1,b:2,c:3}}`);
681 
682     // attribute on type + byKeyValue
683     @serdeLikeStruct
684     static struct B
685     {
686         D obj;
687         alias obj this;
688     }
689 
690     assert(B().serializeText == `{a:1,b:2,c:3}`);
691 }
692 
693 ///
694 version(mir_ion_test) unittest
695 {
696     import mir.ser.json;
697     import mir.deser.json;
698     import std.range;
699     import std.algorithm;
700     import std.conv;
701 
702     static struct S
703     {
704         @serdeTransformIn!"a += 2"
705         @serdeTransformOut!(a =>"str".repeat.take(a).joiner("_").to!string)
706         int a;
707     }
708 
709     auto s = deserializeJson!S(`{"a":3}`);
710     assert(s.a == 5);
711     assert(serializeJson(s) == `{"a":"str_str_str_str_str"}`);
712 }
713 
714 ///
715 @safe pure
716 version(mir_ion_test) unittest
717 {
718     static struct Toto  
719     {
720         import mir.ndslice.slice: Slice;
721 
722         Slice!(int*, 2) a;
723         Slice!(int*, 1) b;
724 
725         this(int x) @safe pure nothrow
726         {
727             import mir.ndslice.topology: iota, repeat;
728             import mir.ndslice.allocation: slice;
729 
730             a = [2, 3].iota!int.slice;
731             b = repeat(x, [2]).slice;
732         }
733     }
734 
735     auto toto = Toto(5);
736 
737     import mir.ser.json: serializeJson;
738     import mir.deser.json: deserializeJson;
739 
740     auto description = toto.serializeJson!Toto;
741     assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}});
742     assert(toto == description.deserializeJson!Toto);
743 }
744 
745 ///
746 @safe pure @nogc
747 version(mir_ion_test) unittest
748 {
749     static struct Toto  
750     {
751         import mir.ndslice.slice: Slice;
752         import mir.rc.array: RCI;
753 
754         Slice!(RCI!int, 2) a;
755         Slice!(RCI!int, 1) b;
756 
757         this(int x) @safe pure nothrow @nogc
758         {
759             import mir.ndslice.topology: iota, repeat;
760             import mir.ndslice.allocation: rcslice;
761 
762             a = [2, 3].iota!int.rcslice;
763             b = repeat(x, [2]).rcslice;
764         }
765     }
766 
767     auto toto = Toto(5);
768 
769     import mir.ser.json: serializeJson;
770     import mir.deser.json: deserializeJson;
771     import mir.format: stringBuf;
772 
773     auto buffer = stringBuf;
774     serializeJson(buffer, toto);
775     auto description = buffer.data;
776     assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}});
777     assert(toto == description.deserializeJson!Toto);
778 }
779 
780 /++
781 User defined algebraic types deserialization supports any subset of the following types:
782 
783 $(UL 
784     $(LI `typeof(null)`)
785     $(LI `bool`)
786     $(LI `long`)
787     $(LI `double`)
788     $(LI `string`)
789     $(LI $(AlgorithmREF timestamp, Timestamp))
790     $(LI `AnyType[]`)
791     $(LI `StringMap!AnyType`)
792     $(LI `AnyType[string]`)
793 )
794 
795 A `StringMap` has has priority over builtin associative arrays.
796 
797 Serializations works with any algebraic types.
798 
799 See_also: $(GMREF mir-core, mir,algebraic), $(GMREF mir-algorithm, mir,string_map)
800 +/
801 version(mir_ion_test) unittest
802 {
803     import mir.string_map;
804     import mir.deser.ion: deserializeIon;
805     import mir.ion.conv: json2ion, ion2text;
806     import mir.algebraic: Algebraic, This;
807 
808     static union Json_
809     {
810         typeof(null) null_;
811         bool boolean;
812         long integer;
813         immutable(char)[] string;
814         double[] array;
815         StringMap!This object;
816     }
817 
818     alias MyJsonAlgebraic = Algebraic!Json_;
819 
820     auto json = `{"b" : true, "z" : null, "this" : {"c" : "str", "d" : [1, 2, 3, 4]}}`;
821     auto binary = json.json2ion;
822     auto value = binary.deserializeIon!MyJsonAlgebraic;
823 
824     auto object = value.get!(StringMap!MyJsonAlgebraic);
825     assert(object["b"].get!bool == true);
826     assert(object["z"].isNull);
827 
828     object = object["this"].get!(StringMap!MyJsonAlgebraic);
829     assert(object["c"].get!string == "str");
830     assert(object["d"].get!(double[]) == [1.0, 2, 3, 4]);
831 }
832 
833 ///
834 version(mir_ion_test) unittest
835 {
836     import mir.algebraic_alias.json;
837     import mir.ser.json: serializeJson;
838     auto value = [JsonAlgebraic[].init.JsonAlgebraic, StringMap!JsonAlgebraic.init.JsonAlgebraic, string.init.JsonAlgebraic];
839     assert(value.serializeJson == `[[],{},""]`, value.serializeJson);
840 }
841 
842 ///
843 version(mir_ion_test) @safe pure unittest
844 {
845     import mir.test;
846     import mir.serde : serdeFallbackStruct;
847     import mir.algebraic;
848     import mir.deser.text;
849     import mir.deser.json;
850     @serdeFallbackStruct struct S { string path; }
851     alias V = Variant!(string, S);
852     static immutable res = [V("str"), V(S("root"))];
853     q{["str", {path: root}]}.deserializeText!(V[]).should == res;
854     q{ [ "str" , { "path" : "root" } ] }.deserializeJson!(V[]).should == res;
855 }
856 
857 /// Date serialization
858 version(mir_ion_test) unittest
859 {
860     import mir.date;
861     import mir.ion.conv: ion2text;
862     import mir.ser.ion: serializeIon;
863     import mir.ser.json: serializeJson;
864     import mir.ser.text: serializeText;
865     assert(Date(2021, 4, 24).serializeIon.ion2text == `2021-04-24`);
866     assert(Date(2021, 4, 24).serializeText == `2021-04-24`);
867     assert(Date(2021, 4, 24).serializeJson == `"2021-04-24"`);
868 }
869 
870 /// Timestamp and LOB support in algebraic types
871 version(mir_ion_test) unittest
872 {
873     import mir.algebraic;
874     import mir.deser.ion: deserializeIon;
875     import mir.ser.ion: serializeIon;
876     import mir.lob;
877     import mir.string_map;
878     import mir.timestamp;
879 
880     static union Ion_
881     {
882         typeof(null) null_;
883         bool boolean;
884         long integer;
885         double float_;
886         immutable(char)[] string;
887         Blob blob;
888         Clob clob;
889         Timestamp timestamp;
890         This[] array;
891         StringMap!This object;
892     }
893 
894     alias IonAlgebraic = Algebraic!Ion_;
895 
896     StringMap!IonAlgebraic map;
897     map["ts"] = Timestamp(2021, 4, 24);
898     map["clob"] = Clob("Some clob");
899     map["blob"] = Blob([0x32, 0x52]);
900 
901     assert(map.serializeIon.deserializeIon!(StringMap!IonAlgebraic) == map);
902 }
903 
904 /// Phobos date-time serialization
905 version(mir_ion_test) unittest
906 {
907     import core.time : hnsecs, minutes;
908     import mir.ion.conv: ion2text;
909     import mir.ser.ion: serializeIon;
910     import mir.ser.json: serializeJson;
911     import mir.ser.text: serializeText;
912     import mir.deser.ion: deserializeIon;
913     import mir.deser.json: deserializeJson;
914     import std.datetime.date : Date, DateTime;
915     import std.datetime.systime : SysTime;
916     import std.datetime.timezone : SimpleTimeZone;
917 
918     auto date = Date(2021, 4, 24);
919     assert(date.serializeIon.ion2text == `2021-04-24`);
920     assert(date.serializeText == `2021-04-24`);
921     assert(date.serializeJson == `"2021-04-24"`);
922     assert(`"2021-04-24"`.deserializeJson!Date == date);
923 
924     auto datetime = DateTime(1982, 4, 1, 20, 59, 22);
925     assert(datetime.serializeIon.ion2text == `1982-04-01T20:59:22-00:00`);
926     assert(datetime.serializeText == `1982-04-01T20:59:22-00:00`);
927     assert(datetime.serializeJson == `"1982-04-01T20:59:22-00:00"`);
928     assert(`"1982-04-01T20:59:22Z"`.deserializeJson!DateTime == datetime);
929 
930     auto dt = DateTime(1982, 4, 1, 20, 59, 22);
931     auto tz = new immutable SimpleTimeZone(-330.minutes);
932     auto st = SysTime(dt, 1234567.hnsecs, tz);
933     assert(st.serializeIon.ion2text == `1982-04-01T20:59:22.1234567-05:30`);
934     assert(st.serializeText == `1982-04-01T20:59:22.1234567-05:30`);
935     assert(st.serializeJson == `"1982-04-01T20:59:22.1234567-05:30"`);
936     assert(st.serializeIon.deserializeIon!SysTime == st);
937     assert(`"1982-04-01T20:59:22.1234567-05:30"`.deserializeJson!SysTime == st);
938 }
939 
940 version(mir_ion_test) unittest
941 {
942     import mir.deser.json;
943     auto s = q{"n2. Clone theproject_n_n        git clone git://github.com/rej\n2. Clone the project_n_n        git clone git://github.com/rejn"}.deserializeJson!string;
944     assert(s == "n2. Clone theproject_n_n        git clone git://github.com/rej\n2. Clone the project_n_n        git clone git://github.com/rejn", s);
945 }
946 
947 version(mir_ion_test) unittest
948 {
949     import mir.deser.json;
950     assert(q{"n2. Clone theproject_n_n        git clone git://github.com/rej\"). Clone the project_n_n        git clone git://github.com/rejn"}
951         .deserializeJson!string == 
952              "n2. Clone theproject_n_n        git clone git://github.com/rej\"). Clone the project_n_n        git clone git://github.com/rejn");
953 }
954 
955 version (mir_ion_test)
956 unittest
957 {
958     import mir.test;
959     import mir.deser.json;
960     `["\u007F"]`.deserializeJson!(string[]).should == ["\u007F"];
961 }
962 
963 version (mir_ion_test)
964 unittest
965 {
966     import mir.test;
967     import mir.deser.json;
968     `["\uD801\uDC37"]`.deserializeJson!(string[]).should == ["\U00010437"];
969 }
970 
971 /// Static array support
972 @safe pure
973 version(mir_ion_test) unittest
974 {
975     import mir.ser.json;
976     import mir.deser.json;
977     int[3] value = [1, 2, 3];
978     assert(`[1,2,3]`.deserializeJson!(int[3]) == value);
979     assert(value.serializeJson == `[1,2,3]`);
980 }
981 
982 /// ditto
983 @safe pure
984 version(mir_ion_test) unittest
985 {
986     import mir.ser.json;
987     import mir.deser.json;
988     static struct S { int v; }
989     S[3] value = [S(1), S(2), S(3)];
990     auto text = `[{"v":1},{"v":2},{"v":3}]`;
991     assert(text.deserializeJson!(S[3]) == value);
992     assert(value.serializeJson == text);
993 }
994 
995 // AliasThis and serdeProxy
996 version(mir_ion_test) unittest
997 {
998     @serdeProxy!(int[3])
999     static struct J
1000     {
1001         int[3] ar;
1002         alias ar this;
1003     }
1004 
1005     import mir.ser.json;
1006     import mir.deser.json;
1007     auto value = J([1, 2, 3]);
1008     assert(value.serializeJson == `[1,2,3]`);
1009     assert(`[1,2,3]`.deserializeJson!J == value);
1010 }
1011 
1012 
1013 version(mir_ion_test) unittest
1014 {
1015     import mir.ser.text;
1016 
1017     static struct A {
1018         private int _a;
1019         const @property:
1020         int a() @safe pure { return _a; }
1021     }
1022 
1023     auto stra = A(3);
1024 
1025     assert(stra.serializeText == `{a:3}`, stra.serializeText);
1026 }
1027 
1028 ///
1029 version(mir_ion_test) unittest
1030 {
1031     auto json = q{{
1032         "a": [
1033             0.0031
1034         ],
1035         "b": [
1036             0.999475,
1037             0.999425
1038         ]
1039     }};
1040     import mir.deser.json;
1041     static struct S {
1042         double[] a;
1043 
1044         void serdeUnexpectedKeyHandler(scope const(char)[] key) scope @safe pure nothrow @nogc
1045         {
1046             assert(key == "b");
1047         }
1048     }
1049     auto value = json.deserializeJson!(double[][string]);
1050     auto partialStruct = json.deserializeJson!S;
1051 }
1052 
1053 ///
1054 version(mir_ion_test) unittest
1055 {
1056     auto json = q{{
1057         "index": 0.9962125,
1058         "data": 0.0001
1059     }};
1060 
1061     import mir.deser.json;
1062     import mir.series;
1063 
1064     alias T = Observation!(double, double);
1065     auto value = json.deserializeJson!T;
1066     assert(value.index == 0.9962125);
1067     assert(value.data == 0.0001);
1068 }
1069 
1070 ///
1071 version(mir_ion_test) unittest
1072 {
1073     static class MyHugeRESTString
1074     {
1075         string s;
1076 
1077         this(string s)  @safe pure nothrow @nogc
1078         {
1079             this.s = s;
1080         }
1081 
1082         void serialize(S)(scope ref S serializer) scope const
1083         {
1084             auto state = serializer.stringBegin;
1085             // putStringPart is usefull in the loop and with buffers
1086             serializer.putStringPart(s);
1087             serializer.putStringPart(" Another chunk.");
1088             serializer.stringEnd(state);
1089         }
1090 @safe const scope pure nothrow @nogc:
1091 
1092         bool opEquals(scope const typeof(this) rhs)
1093         {
1094             return s == rhs.s;
1095         }
1096 
1097         int opCmp(scope const typeof(this) rhs)
1098         {
1099             return __cmp(s, rhs.s);
1100         }
1101 
1102         override size_t toHash()
1103         {
1104             return hashOf(s);
1105         }
1106     }
1107 
1108     import mir.algebraic: Algebraic, This;
1109     import mir.string_map;
1110 
1111     // Your JSON DOM Type
1112     static union Json_
1113     {
1114         typeof(null) null_;
1115         bool boolean;
1116         long integer;
1117         double float_;
1118         immutable(char)[] string;
1119         MyHugeRESTString restString; 
1120         This[] array;
1121         StringMap!This object;
1122     }
1123     alias Json = Algebraic!Json_;
1124 
1125     /// ordered
1126     StringMap!Json response;
1127     response["type"] = Json("response");
1128     response["data"] = Json(new MyHugeRESTString("First chunk."));
1129 
1130     import mir.ion.conv: ion2text;
1131     import mir.ser.ion;
1132     import mir.ser.json;
1133     import mir.ser.text;
1134 
1135     assert(response.serializeJson == `{"type":"response","data":"First chunk. Another chunk."}`);
1136     assert(response.serializeText == `{type:"response",data:"First chunk. Another chunk."}`);
1137     assert(response.serializeIon.ion2text == `{type:"response",data:"First chunk. Another chunk."}`);
1138 }
1139 
1140 /// Float proxy for integer types
1141 version(mir_ion_test) unittest
1142 {
1143     import mir.deser.json;
1144     static struct S {
1145         @serdeProxy!double
1146         long integralValue;
1147     }
1148 
1149     assert(`{"integralValue":1.23e+2}`.deserializeJson!S.integralValue == 123);
1150 }
1151 
1152 /// Annotated variant
1153 version(mir_ion_test) unittest
1154 {
1155     import mir.algebraic;
1156     import mir.deser.json;
1157     import mir.deser.text;
1158     import mir.ser.json;
1159     import mir.ser.text;
1160 
1161     @serdeAlgebraicAnnotation("s1")
1162     static struct S1 {
1163         string key;
1164     }
1165 
1166     @serdeAlgebraicAnnotation("s2")
1167     static struct S2 {
1168         long key;
1169     }
1170 
1171     alias V = Variant!(S1, S2);
1172     auto v = [V(123), V("124")];
1173 
1174     auto ion = `[s2::{key:123},s1::{key:"124"}]`;
1175     auto json = `[{"s2":{"key":123}},{"s1":{"key":"124"}}]`;
1176     assert(v.serializeText == ion);
1177     assert(ion.deserializeText!(V[2]) == v);
1178     assert(v.serializeJson == json);
1179     assert(json.deserializeJson!(V[2]) == v);
1180 }
1181 
1182 /// Annotated arrays
1183 version(mir_ion_test) unittest
1184 {
1185     import mir.algebraic;
1186     import mir.deser.ion;
1187     import mir.ser.ion;
1188     import mir.ser.text;
1189     import mir.serde;
1190 
1191     @serdeAlgebraicAnnotation("annotation")
1192     @serdeProxy!(long[])
1193     static struct St3 {
1194         long[] arr;
1195         alias arr this;
1196     }
1197     alias V = Variant!St3;
1198 
1199     assert(V(St3([123])).serializeText == `annotation::[123]`);
1200     assert(V(St3([123])).serializeIon.deserializeIon!V == St3([123]));
1201 }
1202 
1203 version(mir_ion_test) @nogc unittest
1204 {
1205     import mir.ion.conv: text2ion;
1206     import mir.deser.ion;
1207     import mir.serde;
1208 
1209     @serdeScoped
1210     @serdeProxy!(const(char)[])
1211     static struct S
1212     {
1213         bool r;
1214         this(scope const(char)[] s) @safe pure nothrow @nogc
1215         {
1216             this.r = s == `simpleSymbol`;
1217         }
1218     }
1219     static immutable data = `simpleSymbol`.text2ion;
1220     assert(data.deserializeIon!S.r);
1221 }
1222 
1223 version(unittest) private
1224 {
1225     import mir.serde: serdeProxy;
1226 
1227     @serdeProxy!ProxyE
1228     enum E
1229     {
1230         none,
1231         bar,
1232     }
1233 
1234     // const(char)[] doesn't reallocate ASDF data.
1235     @serdeProxy!(const(char)[])
1236     struct ProxyE
1237     {
1238         E e;
1239 
1240     @safe pure:
1241 
1242         this(E e)
1243         {
1244             this.e = e;
1245         }
1246 
1247         this(in char[] str) @trusted
1248         {
1249             switch(str)
1250             {
1251                 case "NONE":
1252                 case "NA":
1253                 case "N/A":
1254                     e = E.none;
1255                     break;
1256                 case "BAR":
1257                 case "BR":
1258                     e = E.bar;
1259                     break;
1260                 default:
1261                     throw new Exception("Unknown: " ~ cast(string)str);
1262             }
1263         }
1264 
1265         string toString() scope const
1266         {
1267             if (e == E.none)
1268                 return "NONE";
1269             else
1270                 return "BAR";
1271         }
1272 
1273         E opCast(T : E)()
1274         {
1275             return e;
1276         }
1277     }
1278 
1279     version(mir_ion_test) unittest
1280     {
1281         import mir.ser.json;
1282         import mir.deser.json;
1283 
1284         assert(serializeJson(E.bar) == `"BAR"`);
1285         assert(`"N/A"`.deserializeJson!E == E.none);
1286         assert(`"NA"`.deserializeJson!E == E.none);
1287     }
1288 }
1289 
1290 /// enums
1291 version(mir_ion_test) unittest
1292 {
1293     import std.exception : assertThrown;
1294     import mir.serde: serdeProxy;
1295 
1296     enum Builtin
1297     {
1298         one = 1,
1299         two = 2
1300     }
1301 
1302     enum BuiltinString : string
1303     {
1304         one = "eins",
1305         two = "zwei"
1306     }
1307 
1308     @serdeProxy!int
1309     enum ByInt
1310     {
1311         one = 1,
1312         two = 2
1313     }
1314 
1315     @serdeProxy!string
1316     enum StringEnum : string
1317     {
1318         one = "eins",
1319         two = "zwei"
1320     }
1321 
1322     static struct B
1323     {
1324         Builtin builtin;
1325         BuiltinString bstr;
1326     }
1327 
1328     static struct P
1329     {
1330         ByInt byInt;
1331         StringEnum str;
1332     }
1333 
1334     import mir.ser.json : serializeJson;
1335     import mir.deser.json : deserializeJson;
1336 
1337     assert(`{"builtin":"one","bstr":"one"}`.deserializeJson!B
1338         == B(Builtin.one, BuiltinString.one));
1339     assert(`{"builtin":"two","bstr":"two"}`.deserializeJson!B
1340         == B(Builtin.two, BuiltinString.two));
1341     assertThrown(`{"builtin":"three","bstr":"three"}`.deserializeJson!B);
1342 
1343     assert(`{"byInt":1,"str":"eins"}`.deserializeJson!P
1344         == P(ByInt.one, StringEnum.one));
1345     assert(`{"byInt":2,"str":"zwei"}`.deserializeJson!P
1346         == P(ByInt.two, StringEnum.two));
1347     assertThrown(`{"byInt":2,"str":"drei"}`.deserializeJson!P);
1348     // asserts are a little inconsistent with integral values
1349     assert(`{"byInt":3,"str":"zwei"}`.deserializeJson!P
1350         == P(cast(ByInt)3, StringEnum.two));
1351 
1352 
1353     assert(B().serializeJson
1354         == `{"builtin":"one","bstr":"one"}`);
1355     assert(P().serializeJson
1356         == `{"byInt":1,"str":"eins"}`);
1357 
1358     // serializing invalid enum values may cause AssertError or other errors,
1359     // so avoid doing it altogether.
1360     assertThrown!Throwable(B(cast(Builtin)2, cast(BuiltinString)"drei").serializeJson);
1361     assertThrown!Throwable(B(cast(Builtin)3, BuiltinString.two).serializeJson);
1362     // Fine with @serdeProxy!T with serialization, although inconsistent.
1363     // Use @serdeEnumProxy!T or add @serdeProxyCast to your serdeProxy annotated value
1364     // to also support serialization and deserialization through the underlying type.
1365     assert(P(cast(ByInt)2, cast(StringEnum)"drei").serializeJson
1366         == `{"byInt":2,"str":"StringEnum(drei)"}`);
1367     assert(P(cast(ByInt)3, StringEnum.two).serializeJson
1368         == `{"byInt":3,"str":"zwei"}`);
1369 }
1370 
1371 /// ditto
1372 version(mir_ion_test) unittest
1373 {
1374     import mir.serde: serdeEnumProxy;
1375 
1376     @serdeEnumProxy!int
1377     enum IntE
1378     {
1379         one = 1,
1380         two = 2
1381     }
1382 
1383     @serdeEnumProxy!string
1384     enum StrE : string
1385     {
1386         one = "eins",
1387         two = "zwei"
1388     }
1389 
1390     static struct S
1391     {
1392         IntE a;
1393         StrE b;
1394     }
1395 
1396     import mir.ser.json : serializeJson;
1397     import mir.deser.json : deserializeJson;
1398 
1399     // same as regular serdeProxy so far
1400     assert(`{"a":1,"b":"eins"}`.deserializeJson!S == S(IntE.one, StrE.one));
1401     assert(`{"a":2,"b":"zwei"}`.deserializeJson!S == S(IntE.two, StrE.two));
1402 
1403     assert(`{"a":1,"b":"eins"}` == S(IntE.one, StrE.one).serializeJson);
1404     assert(`{"a":2,"b":"zwei"}` == S(IntE.two, StrE.two).serializeJson);
1405 
1406     // but allows any underlying values
1407     assert(`{"a":3,"b":"drei"}`.deserializeJson!S
1408         == S(cast(IntE)3, cast(StrE)"drei"));
1409     assert(`{"a":3,"b":"drei"}`
1410         == S(cast(IntE)3, cast(StrE)"drei").serializeJson,
1411         S(cast(IntE)3, cast(StrE)"drei").serializeJson);
1412 }
1413 
1414 version(mir_ion_test) unittest
1415 {
1416     import mir.serde: serdeEnumProxy;
1417     import mir.algebraic: Nullable, Variant, nullable;
1418 
1419     @serdeEnumProxy!string
1420     enum StrE : string
1421     {
1422         one = "eins",
1423         two = "zwei"
1424     }
1425 
1426     static struct S
1427     {
1428         Nullable!(StrE[]) a;
1429         Variant!(void, StrE) b;
1430     }
1431 
1432     import mir.ser.json : serializeJson;
1433     import mir.deser.json : deserializeJson;
1434 
1435     auto s = S([StrE.one, StrE.two, cast(StrE)"drei"].nullable);
1436     s.b = StrE.two;
1437     auto str = `{"a":["eins","zwei","drei"],"b":"zwei"}`;
1438 
1439     assert(str.deserializeJson!S == s);
1440     assert(s.serializeJson == str, str);
1441 }
1442 
1443 // check symbols support
1444 // check typed nullable support
1445 // check void support
1446 version(mir_ion_test) unittest
1447 {
1448     import mir.algebraic_alias.ion;
1449     import mir.algebraic;
1450     import mir.annotated;
1451     import mir.deser.text;
1452     import mir.ser.text;
1453     import mir.ion.type_code;
1454     import mir.ion.value: IonNull;
1455     import mir.small_string;
1456     import mir.timestamp;
1457     assert(`aSymbol`.deserializeText!(Nullable!(bool, double, long, string, Timestamp)) == `aSymbol`);
1458     assert(`aSymbol`.deserializeText!(Nullable!(bool, double, long, SmallString!32, Timestamp)) == `aSymbol`.SmallString!32);
1459     assert(`null.timestamp`.deserializeText!(Nullable!Timestamp) == null);
1460     assert(`null.timestamp`.deserializeText!(Nullable!(bool, Timestamp)) == null);
1461     //IonNull support
1462     assert(`null.timestamp`.deserializeText!(Variant!(IonNull, bool, Timestamp)) == IonTypeCode.timestamp.IonNull);
1463     //Annotated support
1464     auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01"));
1465     auto variant = `birthday::2001-01-01`.deserializeText!IonAlgebraic;
1466     // assert(variant == annotated);
1467     assert(Nullable!(void, int)(3).serializeText == "3");
1468 }
1469 
1470 version(mir_ion_test) unittest
1471 {
1472     import mir.deser.text;
1473     import mir.ser.text;
1474     enum CaseSensitive
1475     {
1476         no,
1477         yes,
1478         osDefault = yes,
1479     }
1480 
1481     assert(CaseSensitive.osDefault.serializeText == `yes`);
1482     assert(`yes`.deserializeText!CaseSensitive == CaseSensitive.yes);
1483 
1484     enum TextAlignment : uint {
1485         Left = 0, ///
1486         Center = 1, ///
1487         Right = 2, ///
1488 
1489         VerticalTop = 0, ///
1490         VerticalCenter = 4, ///
1491         VerticalBottom = 8, ///
1492     }
1493 
1494     assert(TextAlignment.VerticalTop.serializeText == `Left`);
1495     assert(`Left`.deserializeText!TextAlignment == TextAlignment.Left);
1496 }
1497 
1498 version(mir_ion_test) unittest
1499 {
1500     import mir.ndslice.topology: iota, as;
1501     auto range = as!char(3.iota + 'a');
1502     assert(range == "abc");
1503     import mir.ser.text;
1504     assert(range.serializeText == `"abc"`);
1505 }
1506 
1507 /// Series de/serialization
1508 @safe version(mir_ion_test) unittest
1509 {
1510     import mir.test;
1511     import mir.deser.text;
1512     import mir.ser.text;
1513     import mir.series;
1514     auto s = ["a", "b"].series([5, 6]);
1515     auto t = `{index:["a","b"],data:[5,6]}`;
1516     auto r = `{index:["b","a"],data:[6,5]}`;
1517     s.serializeText.should == t;
1518     assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText);
1519 }
1520 
1521 // Const series de/serialization
1522 @safe version(mir_ion_test) unittest
1523 {
1524     import mir.deser.text;
1525     import mir.ser.text;
1526     import mir.series;
1527     auto s = ["a", "b"].series([5, 6]).lightConst;
1528     auto t = `{index:["a","b"],data:[5,6]}`;
1529     auto r = `{index:["b","a"],data:[6,5]}`;
1530     assert(s.serializeText == t);
1531     assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText);
1532 }
1533 
1534 /// RC Series de/serialization
1535 @safe version(mir_ion_test) unittest
1536 {
1537     import mir.test;
1538     import mir.deser.text;
1539     import mir.ser.text;
1540     import mir.ndslice.allocation: rcslice;
1541     import mir.series;
1542     import mir.small_string;
1543     auto s = [SmallString!32("a"), SmallString!32("b")].rcslice.series([5, 6].rcslice);
1544     auto t = `{index:["a","b"],data:[5,6]}`;
1545     auto r = `{index:["b","a"],data:[6,5]}`;
1546     assert(s.serializeText == t);
1547     assert(r.deserializeText!(typeof(s)) == s);
1548 }
1549 
1550 // Const RC Series de/serialization
1551 @safe version(mir_ion_test) unittest
1552 {
1553     import mir.deser.text;
1554     import mir.ser.text;
1555     import mir.ndslice.allocation: rcslice;
1556     import mir.series;
1557     import mir.small_string;
1558     auto s = [SmallString!32("a"), SmallString!32("b")].rcslice.series([5, 6].rcslice).lightConst;
1559     auto t = `{index:["a","b"],data:[5,6]}`;
1560     auto r = `{index:["b","a"],data:[6,5]}`;
1561     assert(s.serializeText == t);
1562     assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText);
1563 }
1564 
1565 version(mir_ion_test)
1566 unittest
1567 {
1568     import std.exception: assertThrown;
1569     import mir.deser.json;
1570     static struct S {}
1571     ",".deserializeJson!S.assertThrown!Exception;
1572 }
1573 
1574 ///
1575 @safe pure version(mir_ion_test) unittest
1576 {
1577     import mir.algebraic: Nullable;
1578     import mir.ser.text: serializeText;
1579     import mir.deser.text: deserializeText;
1580     import mir.serde: serdeDiscriminatedField;
1581 
1582     @serdeDiscriminatedField("tag", "data")
1583     static struct D
1584     {
1585         int num;
1586     }
1587 
1588     @serdeDiscriminatedField("tag", "gr")
1589     static struct G
1590     {
1591         string val;
1592     }
1593 
1594     alias V = Nullable!(string, D, G);
1595 
1596     assert(D(123).serializeText == `{tag:data,num:123}`);
1597     assert(V(D(123)).serializeText == `{tag:data,num:123}`);
1598     assert(`{tag:data,num:123}`.deserializeText!V == D(123));
1599     assert(`{val:"str",tag:gr}`.deserializeText!V == G("str"));
1600 }
1601 
1602 
1603 ///
1604 version(mir_ion_test)
1605 unittest
1606 {
1607     import mir.ser.text;
1608     import mir.deser.text;
1609 
1610     @serdeProxy!int
1611     enum Foo
1612     {
1613         a = 1,
1614         b = 2
1615     }
1616 
1617     assert((cast(Foo)6).serializeText == "6", Foo.b.serializeText);
1618     assert("6".deserializeText!Foo == 6);
1619 }
1620 
1621 /// @serdeOrderedIn support
1622 version(mir_ion_test)
1623 @safe pure
1624 unittest
1625 {
1626     import mir.deser.text;
1627     import mir.serde: serdeOrderedIn;
1628     import mir.test: should;
1629 
1630     @serdeOrderedIn
1631     static struct Foo
1632     {
1633         int a;
1634         private int b_;
1635 
1636         auto b(int value) @property
1637         {
1638             b_ = value;
1639             // a is always set here
1640             assert(a + b_ == 7);
1641         }
1642     }
1643 
1644     `{b:3,a:4}`.deserializeText!Foo.should == Foo(4, 3);
1645 }
1646 
1647 /// `new C` support for classes
1648 version(mir_ion_test)
1649 @safe pure
1650 unittest
1651 {
1652     import mir.deser.text;
1653     import mir.test: should;
1654 
1655     static class C
1656     {
1657         int a;
1658         this() @safe pure {
1659         }
1660     }
1661 
1662     `{a:4}`.deserializeText!C.a.should == 4;
1663 }
1664 
1665 /// Tuple support
1666 unittest
1667 {
1668     import mir.test: should;
1669     import mir.functional: Tuple;
1670     import mir.ser.text: serializeText;
1671     import mir.deser.text: deserializeText;
1672 
1673     alias T = Tuple!(uint, string, double[]);
1674     auto value = T(3, "str", [3.4, 23]);
1675     auto text = `[3,"str",[3.4,23.0]]`;
1676     value.serializeText.should == text;
1677     text.deserializeText!T.should == value;
1678 
1679     // combination with Algebraic
1680     import mir.algebraic: Variant;
1681     alias V = Variant!(T, T[string], T);
1682 
1683     text.deserializeText!V.should == value;
1684     `{key: [3,"str",[3.4,23.0]]}`
1685         .deserializeText!V
1686         .get!(T[string])["key"]
1687         .should == value;
1688 }
1689 
1690 version(mir_ion_test)
1691 @safe pure
1692 unittest
1693 {
1694     import mir.algebraic : Variant;
1695     import mir.deser.text;
1696     import mir.ser.text;
1697     import mir.serde;
1698     import mir.test;
1699 
1700     enum SerialContract {serial, contract} 
1701 
1702     @serdeAnnotation
1703     @serdeAlgebraicAnnotation("stirFuture")
1704     static struct StirFutureConfig
1705     {
1706         @serdeAnnotation
1707         SerialContract kind;
1708 
1709         int data;
1710         alias data this;
1711     }
1712 
1713     auto config = Variant!StirFutureConfig(SerialContract.contract, 7);
1714     auto str = "stirFuture::contract::7";
1715     config.serializeText.should == "stirFuture::contract::7";
1716     `stirFuture::contract::7`
1717         .deserializeText!(Variant!StirFutureConfig).should == config;
1718 }
1719 
1720 version(mir_ion_test)
1721 @safe pure
1722 unittest
1723 {
1724     import mir.deser.text;
1725     import mir.serde;
1726     @serdeIgnoreCase enum E { a, b }
1727     assert (`b`.deserializeText!E == E.b);
1728     assert (`B`.deserializeText!E == E.b);
1729 }
1730 
1731 version(mir_ion_test)
1732 @safe pure
1733 unittest
1734 {
1735     import std.exception: assertThrown;
1736     import mir.ion.conv;
1737     "\"".json2ion.assertThrown;
1738     "??:? [0x55d24c502acd]".json2ion.assertThrown;
1739     "??:? [0x55d24c502acd]\"".json2ion.assertThrown;
1740     "??:? [0x55d24c502acd]\"}".json2ion.assertThrown;
1741 }