1 /++
2 $(H4 High level deserialization API)
3 
4 Macros:
5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP)
6 +/
7 module mir.deser;
8 
9 import mir.deser.low_level;
10 import mir.internal.meta: hasUDA, getUDAs;
11 import mir.ion.exception;
12 import mir.ion.internal.basic_types: isTuple;
13 import mir.ion.symbol_table;
14 import mir.ion.type_code;
15 import mir.ion.value;
16 import mir.serde: serdeGetFinalProxy;
17 import mir.small_array;
18 import mir.small_string;
19 import mir.utility: _expect;
20 import std.traits: ForeachType, Unqual, isSomeChar, EnumMembers, TemplateArgsOf;
21 
22 private alias AliasSeq(T...) = T;
23 
24 public import mir.serde;
25 
26 private enum isSmallString(T) = is(T == SmallString!N, size_t N);
27 
28 package template hasScoped(T)
29 {
30     import std.traits: isAggregateType;
31     import mir.serde: serdeScoped;
32     static if (is(T == enum) || isAggregateType!T)
33         enum hasScoped = hasUDA!(T, serdeScoped);
34     else
35         enum hasScoped = false;
36 }
37 
38 IonException deserializeValue_(T)(scope IonDescribedValue data, return scope ref T value)
39     if (isFirstOrderSerdeType!T)
40 {
41     return deserializeValueImpl(data, value).ionException;
42 }
43 
44 IonException deserializeValue_(T, P...)(scope IonDescribedValue data, return scope ref T value, scope const P p)
45     if (isFirstOrderSerdeType!T)
46 {
47     return deserializeValue_!T(data, value);
48 }
49 
50 static immutable exc(T, string member, int line = __LINE__) = new IonException("mir.ion: non-optional member '" ~ member ~ "' in " ~ T.stringof ~ " is missing.", __FILE__, line);
51 static immutable excm(T, string member, int line = __LINE__) = new IonException("mir.ion: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed.", __FILE__, line);
52 
53 static immutable cantConstructNullValueOfType(T, int line = __LINE__) = new IonException("Can't construct null value of type" ~ T.stringof, __FILE__, line);
54 static immutable cantConstructObjectExc(T, int line = __LINE__) = new IonException(T.stringof ~ " must be either not null or have a default `@safe pure` constructor.", __FILE__, line);
55 static immutable cantDeserilizeTFromIonStruct(T, int line = __LINE__) = new IonException("Can't deserilize " ~ T.stringof ~ " from IonStruct", __FILE__, line);
56 static immutable cantDesrializeUnexpectedDescriptorType(T, int line = __LINE__) = new IonException("Can't desrialize " ~ T.stringof ~ ". Unexpected descriptor type.", __FILE__, line);
57 static immutable unexpectedAnnotationWhenDeserializing(T, int line = __LINE__) = new IonException("Unexpected annotation when deserializing " ~ T.stringof, __FILE__, line);
58 static immutable unexpectedIonTypeCodeFor(T, int line = __LINE__) = new IonException("Unexpected IonTypeCode for " ~ T.stringof, __FILE__, line);
59 static immutable unexpectedKeyWhenDeserializing(T, int line = __LINE__) = new IonException("Unexpected key when deserializing " ~ T.stringof, __FILE__, line);
60 static immutable unexpectedSymbolIdWhenDeserializing(T, int line = __LINE__) = new IonException("Unexpected symbol ID when deserializing " ~ T.stringof, __FILE__, line);
61 static immutable unusedAnnotation(T, int line = __LINE__) = new IonException("Unused annotation for " ~ T.stringof, __FILE__, line);
62 
63 ///
64 enum TableKind
65 {
66     ///
67     scopeRuntime,
68     ///
69     immutableRuntime,
70 }
71 
72 package static immutable tableInsance(string[] symbolTable) = symbolTable;
73 
74 package template hasDeserializeFromIon(T)
75 {
76     import std.traits: isAggregateType;
77     import std.meta: staticIndexOf;
78     static if (isAggregateType!T)
79         enum hasDeserializeFromIon = staticIndexOf!("deserializeFromIon", __traits(allMembers, T)) >= 0;
80     else
81         enum hasDeserializeFromIon = false;
82 }
83 
84 /++
85 Deserialize aggregate value using compile time symbol table
86 +/
87 template deserializeValue(string[] symbolTable, TableKind tableKind)
88 {
89     import mir.appender: scopedBuffer, ScopedBuffer;
90 
91     static if (tableKind == TableKind.scopeRuntime)
92         alias RuntimeSymbolTable = const(char[])[];
93     else
94         alias RuntimeSymbolTable = const(string)[];
95 
96 @safe pure:
97 
98     @safe pure nothrow @nogc
99     private bool prepareSymbolId(
100         scope ref size_t symbolId,
101         scope const(uint)[] tableIndex,
102         )
103     {
104         if (symbolId >= tableIndex.length)
105             return false;
106         symbolId = tableIndex[symbolId];
107         return symbolId < symbolTable.length;
108     }
109 
110     @trusted nothrow @nogc private IonException deserializeScoped(C)(
111         scope IonDescribedValue data,
112         scope ref C[] value,
113         scope RuntimeSymbolTable table,
114         scope const(uint)[] tableIndex,
115         )
116         if (is(immutable C == immutable char))
117     {
118 
119         import std.traits: Select;
120         import mir.serde: serdeGetProxy, serdeScoped, serdeScoped;
121         import mir.conv: to;
122 
123 
124         if (data.descriptor.type == IonTypeCode.symbol)
125         {
126             size_t id;
127             if (auto exc = data.trustedGet!IonSymbolID.get(id))
128                 return exc.ionException;
129             if (id >= table.length)
130                 return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
131             (()@trusted pure {value = cast(C[])table[id];})();
132             return null;
133         }
134         else
135         {
136             if (_expect(data.descriptor.type != IonTypeCode..string && data.descriptor.type != IonTypeCode.null_, false))
137                 return IonErrorCode.expectedStringValue.ionException;
138             auto ionValue = data.trustedGet!(const(char)[]);
139             value = cast(C[])ionValue;
140             return null;
141         }
142     }
143 
144     private IonException deserializeListToScopedBuffer(Buffer)(
145         scope IonDescribedValue data,
146         scope ref Buffer buffer,
147         scope RuntimeSymbolTable table,
148         scope const(uint)[] tableIndex,
149         )
150     {
151         if (_expect(data.descriptor.type != IonTypeCode.list, false))
152             return IonErrorCode.expectedListValue.ionException;
153         foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
154         {
155             import std.traits: Unqual;
156             if (_expect(error, false))
157                 return error.ionException;
158             Unqual!(typeof(buffer.data[0])) value;
159             if (auto exception = deserializeValue(ionElem, value, table, tableIndex))
160                 return exception;
161             import core.lifetime: move;
162             buffer.put(move(value));
163         }
164         return null;
165     }
166 
167     private IonException deserializeValueMember(string member, T)(
168         scope IonDescribedValue data,
169         return scope ref T value,
170         scope ref SerdeFlags!T requiredFlags,
171         scope RuntimeSymbolTable table,
172         scope const(uint)[] tableIndex,
173         )
174     {
175         import core.lifetime: move;
176         import mir.conv: to;
177         import mir.reflection: hasField;
178 
179         enum likeList = hasUDA!(T, member, serdeLikeList);
180         enum likeStruct  = hasUDA!(T, member, serdeLikeStruct);
181         enum hasProxy = hasUDA!(T, member, serdeProxy);
182 
183         alias Member = serdeDeserializationMemberType!(T, member);
184 
185         static if (hasProxy)
186             alias Temporal = serdeGetProxy!(T, member);
187         else
188             alias Temporal = Member;
189 
190         enum hasScoped = hasUDA!(T, member, serdeScoped) || hasScoped!Temporal;
191 
192         enum hasTransform = hasUDA!(T, member, serdeTransformIn);
193 
194         static if (hasTransform)
195             alias transform = serdeGetTransformIn!(__traits(getMember, value, member));
196 
197         static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes");
198         static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
199         static assert (hasProxy >= likeList, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
200 
201         static if (hasScoped)
202         {
203             static if (is(immutable Temporal == immutable char[]))
204             {
205                 alias impl = deserializeScoped;
206             }
207             else
208             {
209                 alias impl = deserializeValue;
210             }
211         }
212         else
213         {
214             alias impl = deserializeValue;
215         }
216 
217         static if (!hasUDA!(T, member, serdeAllowMultiple))
218             if (__traits(getMember, requiredFlags, member))
219                 return unqualException(excm!(T, member));
220 
221         __traits(getMember, requiredFlags, member) = true;
222 
223         static if (likeList)
224         {
225             if (data.descriptor.type == IonTypeCode.list)
226             {
227                 foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
228                 {
229                     if (_expect(error, false))
230                         return error.ionException;
231                     Temporal elem;
232                     if (auto exception = impl(ionElem, elem, table, tableIndex))
233                         return exception;
234                     import core.lifetime: move;
235                     __traits(getMember, value, member).put(move(elem));
236                 }
237             }
238             else
239             if (data.descriptor.type == IonTypeCode.null_)
240             {
241             }
242             else
243             {
244                 return IonErrorCode.expectedListValue.ionException;
245             }
246             static if (hasTransform)
247             {
248                 static if (hasField!(T, member))
249                 {
250                     transform(__traits(getMember, value, member));
251                 }
252                 else
253                 {
254                     auto temporal = __traits(getMember, value, member);
255                     transform(temporal);
256                     __traits(getMember, value, member) = move(temporal);
257                 }
258             }
259             return null;
260         }
261         else
262         static if (likeStruct)
263         {
264             if (data.descriptor.type == IonTypeCode.struct_)
265             {
266                 foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue ionElem; data.trustedGet!IonStruct)
267                 {
268                     if (_expect(error, false))
269                         return error.ionException;
270 
271                     Temporal elem;
272                     if (auto exception = impl(ionElem, elem, table, tableIndex))
273                         return exception;
274                     import core.lifetime: move;
275 
276 
277                     if (symbolId >= table.length)
278                         return unqualException(unexpectedSymbolIdWhenDeserializing!T);
279 
280                     static if (tableKind == TableKind.immutableRuntime)
281                     {
282                         (()@trusted pure {__traits(getMember, value, member)[table[symbolId]] = move(elem);})();
283                     }
284                     else
285                     {
286                         __traits(getMember, value, member)[table[symbolId].idup] = move(elem);
287                     }
288                 }
289             }
290             else
291             if (data.descriptor.type == IonTypeCode.null_)
292             {
293             }
294             else
295             {
296                 return IonErrorCode.expectedStructValue.ionException;
297             }
298             static if (hasTransform)
299             {
300                 static if (hasField!(T, member))
301                 {
302                     transform(__traits(getMember, value, member));
303                 }
304                 else
305                 {
306                     auto temporal2 = __traits(getMember, value, member);
307                     transform(temporal2);
308                     __traits(getMember, value, member) = move(temporal2);
309                 }
310             }
311             return null;
312         }
313         else
314         static if (hasProxy)
315         {
316             Temporal proxy;
317             if (auto exception = impl(data, proxy, table, tableIndex))
318                 return exception;
319             auto temporal = to!(serdeDeserializationMemberType!(T, member))(move(proxy));
320             static if (hasTransform)
321                 transform(temporal);
322             __traits(getMember, value, member) = move(temporal);
323             return null;
324         }
325         else
326         static if (hasField!(T, member))
327         {
328             if (auto exception = impl(data, __traits(getMember, value, member), table, tableIndex))
329                 return exception;
330             static if (hasTransform)
331                 transform(__traits(getMember, value, member));
332             return null;
333         }
334         else
335         {
336             static if (hasScoped && is(Member == D[], D) && !is(Unqual!D == char))
337             {
338                 import std.traits: hasIndirections;
339                 static if (!hasIndirections!D)
340                 {
341                     alias E = Unqual!D;
342                     auto buffer = scopedBuffer!E;
343                 }
344                 else
345                 {
346                     import std.array: std_appender = appender;
347                     auto buffer = std_appender!(D[]);
348                 }
349                 if (auto exception = deserializeListToScopedBuffer(data, buffer, table, tableIndex))
350                     return exception;
351                 auto temporal = (() @trusted => cast(Member)buffer.data)();
352                 static if (hasTransform)
353                     transform(temporal);
354                 __traits(getMember, value, member) = move(temporal);
355                 return null;
356             }
357             else
358             {
359                 Member temporal;
360                 if (auto exception = impl(data, temporal, table, tableIndex))
361                     return exception;
362                 static if (hasTransform)
363                     transform(temporal);
364                 __traits(getMember, value, member) = move(temporal);
365                 return null;
366             }
367         }
368     }
369 
370     import mir.algebraic: isVariant;
371 
372     /++
373     Deserialize aggregate value
374     Params:
375         value = value to deserialize
376     Returns: `IonException`
377     +/
378     IonException deserializeValue(T, Annotations...)(
379         scope IonDescribedValue data,
380         return scope ref T value,
381         scope RuntimeSymbolTable table,
382         scope const(uint)[] tableIndex,
383         scope Annotations annotations_,
384         )
385         if (!isFirstOrderSerdeType!T && !isVariant!T && Annotations.length <= 1)
386     {
387         import mir.algebraic: isVariant, isNullable;
388         import mir.internal.meta: Contains;
389         import mir.ndslice.slice: Slice, SliceKind;
390         import mir.rc.array: RCArray, RCI;
391         import mir.reflection: isStdNullable;
392         import mir.string_map : isStringMap;
393         import std.meta: anySatisfy, Filter, templateAnd, templateNot, templateOr, ApplyRight;
394         import std.traits: isArray, isSomeString, isAssociativeArray;
395 
396 
397         static if (hasDeserializeFromIon!T)
398         {
399             static if (tableKind == TableKind.immutableRuntime)
400                 return value.deserializeFromIon((()@trusted =>table)(), data);
401             else
402                 return value.deserializeFromIon(table, data);
403         }
404         else
405         static if (is(T : SmallArray!(E, maxLength), E, size_t maxLength))
406         {
407             if (data.descriptor.type == IonTypeCode.list)
408             {
409                 foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
410                 {
411                     if (_expect(error, false))
412                         return error.ionException;
413                     if (value._length == maxLength)
414                         return IonErrorCode.smallArrayOverflow.ionException;
415                     E elem;
416                     if (auto exception = deserializeValue(ionElem, elem, table, tableIndex))
417                         return exception;
418                     import core.lifetime: move;
419                     value.trustedAppend(move(elem));
420                 }
421                 return null;
422             }
423             else
424             if (data.descriptor.type == IonTypeCode.null_)
425             {
426                 return null;
427             }
428             return IonErrorCode.expectedListValue.ionException;
429         }
430         else
431         static if (is(T == string) || is(T == const(char)[]) || is(T == char[]))
432         {
433             if (data.descriptor.type == IonTypeCode.symbol)
434             {
435                 size_t id;
436                 if (auto exc = data.trustedGet!IonSymbolID.get(id))
437                     return exc.ionException;
438                 if (id >= table.length)
439                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
440                 import mir.conv: to;
441                 static if (tableKind == TableKind.scopeRuntime && !is(T == string))
442                     value = table[id].dup;
443                 else
444                 static if (tableKind == TableKind.scopeRuntime)
445                     value = table[id].idup;
446                 else
447                     ()@trusted pure {value = table[id].to!T; }();
448                 return null;
449             }
450             if (_expect(data.descriptor.type != IonTypeCode..string && data.descriptor.type != IonTypeCode.null_, false))
451                 return IonErrorCode.expectedStringValue.ionException;
452             auto ionValue = data.trustedGet!(const(char)[]);
453             static if (is(T == string))
454                 value = ionValue.idup;
455             else
456                 value = ionValue.dup;
457             return null; 
458         }
459         else
460         static if (is(T : SmallString!maxLength, size_t maxLength))
461         {
462             if (data.descriptor.type == IonTypeCode.symbol)
463             {
464                 size_t id;
465                 if (auto exc = data.trustedGet!IonSymbolID.get(id))
466                     return exc.ionException;
467                 if (id >= table.length)
468                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
469                 value = table[id];
470                 return null;
471             }
472             if (_expect(data.descriptor.type != IonTypeCode..string && data.descriptor.type != IonTypeCode.null_, false))
473                 return IonErrorCode.expectedStringValue.ionException;
474             auto ionValue = data.trustedGet!(const(char)[]);
475             if (ionValue.length > maxLength)
476                 return IonErrorCode.smallStringOverflow.ionException;
477             value.trustedAssign(ionValue);
478             return null; 
479         }
480         else
481         static if (is(T == RCArray!RC, RC) && isSomeChar!RC)
482         {
483             import mir.rc.array: rcarray;
484             if (data.descriptor.type == IonTypeCode.symbol)
485             {
486                 size_t id;
487                 if (auto exc = data.trustedGet!IonSymbolID.get(id))
488                     return exc.ionException;
489                 if (id >= table.length)
490                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
491                 (()@trusted pure {value = table[id].rcarray!(TemplateArgsOf!T);})();
492                 return null;
493             }
494             import std.traits: TemplateArgsOf;
495             if (_expect(data.descriptor.type != IonTypeCode..string && data.descriptor.type != IonTypeCode.null_, false))
496                 return IonErrorCode.expectedStringValue.ionException;
497             auto ionValue = data.trustedGet!(const(char)[]);
498             value = ionValue.rcarray!(TemplateArgsOf!T);
499             return null; 
500         }
501         else
502         static if (is(T == D[], D))
503         {
504             if (data.descriptor.type == IonTypeCode.list)
505             {
506                 import std.array: std_appender = appender;
507                 auto buffer = std_appender!(D[]);
508                 if (auto exception = deserializeListToScopedBuffer(data, buffer, table, tableIndex))
509                     return exception;
510                 value = buffer.data;
511                 return null;
512             }
513             else
514             if (data.descriptor.type == IonTypeCode.null_)
515             {
516                 value = null;
517                 return null;
518             }
519             return IonErrorCode.expectedListValue.ionException;
520         }
521         else
522         static if (is(T == D[N], D, size_t N))
523         {
524             if (data.descriptor.type != IonTypeCode.list)
525                 return IonErrorCode.expectedListValue.ionException;
526             size_t i;
527             foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
528             {
529                 if (_expect(error, false))
530                     return error.ionException;
531                 if (i >= N)
532                     return IonErrorCode.tooManyElementsForStaticArray.ionException;
533                 if (auto exception = deserializeValue(ionElem, value[i++], table, tableIndex))
534                     return exception;
535             }
536             if (i < N)
537                 return IonErrorCode.notEnoughElementsForStaticArray.ionException;
538             return null;
539         }
540         else
541         static if (isTuple!T)
542         {
543             enum N = value.expand.length;
544             if (data.descriptor.type != IonTypeCode.list)
545                 return IonErrorCode.expectedListValue.ionException;
546             size_t i;
547             foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
548             {
549                 if (_expect(error, false))
550                     return error.ionException;
551                 S: switch (i++)
552                 {
553                     static foreach (j; 0 .. N)
554                     {
555                         case j:
556                             if (auto exception = deserializeValue(ionElem, value[j], table, tableIndex))
557                                 return exception;
558                             break S;
559                     }
560                     default:
561                         return IonErrorCode.tooManyElementsForStaticArray.ionException;
562                 }
563             }
564             if (i < N)
565                 return IonErrorCode.notEnoughElementsForStaticArray.ionException;
566             return null;
567         }
568         else
569         static if (is(T == V[K], K, V))
570         {
571             import mir.conv;
572             if (data.descriptor.type != IonTypeCode.null_ && data.descriptor.type != IonTypeCode.struct_)
573                 return IonErrorCode.expectedIonStructForAnAssociativeArrayDeserialization.ionException;
574             if (data.descriptor.L == 0xF)
575             {
576                 value = null;
577                 return null;
578             }
579             auto ionValue = data.trustedGet!IonStruct;
580 
581             foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; ionValue)
582             {
583                 if (error)
584                     return error.ionException;
585                 if (symbolId >= table.length)
586                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
587                 import mir.conv: to;
588                 if (auto errorMsg = deserializeValue(elem, ref () @trusted pure {return value.require(table[symbolId].to!K);} (), table, tableIndex))
589                     return errorMsg;
590             }
591             return null;
592         }
593         else
594         static if (isStringMap!T)
595         {
596             if (data.descriptor.type != IonTypeCode.null_ && data.descriptor.type != IonTypeCode.struct_)
597                 return IonErrorCode.expectedIonStructForAnAssociativeArrayDeserialization.ionException;
598             if (data.descriptor.L == 0xF)
599             {
600                 value = null;
601                 return null;
602             }
603             auto ionValue = data.trustedGet!IonStruct;
604 
605             IonErrorCode lengthError;
606             auto length = ionValue.walkLength;
607             if (lengthError)
608                 return lengthError.ionException;
609             auto keys = new string[length];
610             auto values = new typeof(value[string.init])[length];
611             size_t i;
612             foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; ionValue)
613             {
614                 if (error)
615                     return error.ionException;
616                 if (symbolId >= table.length)
617                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
618                 static if (tableKind == TableKind.immutableRuntime)
619                     keys[i] = table[symbolId];
620                 else
621                     keys[i] = table[symbolId].idup;
622                 if (auto exception = deserializeValue(elem, values[i], table, tableIndex))
623                     return exception;
624                 i++;
625             }
626             value = T(keys, values);
627             return null;
628         }
629         else
630         static if (is(T == Slice!(D*, N, kind), D, size_t N, SliceKind kind))
631         {
632             import mir.ndslice.topology: asKindOf;
633 
634             static if (N == 1)
635             {
636                 import mir.ndslice.slice: sliced;
637 
638                 D[] array;
639                 if (auto ret = deserializeValue(data, array, table, tableIndex))
640                     return ret;
641                 value = array.sliced.asKindOf!kind;
642                 return null;
643             }
644             // TODO: create a single allocation algorithm
645             else
646             {
647                 import mir.ndslice.fuse: fuse;
648 
649                 Slice!(D*, N - 1)[] array;
650                 if (auto ret = deserializeValue(data, array, table, tableIndex))
651                     return ret;
652                 value = array.fuse.asKindOf!kind;
653                 return null;
654             }
655         }
656         else
657         static if (is(T == Slice!(RCI!D, N, kind), D, size_t N, SliceKind kind))
658         {
659             import mir.ndslice.topology: asKindOf;
660 
661             static if (N == 1)
662             {
663                 RCArray!D array;
664                 if (auto ret = deserializeValue(data, array, table, tableIndex))
665                     return ret;
666                 () @trusted {
667                     value = array.moveToSlice.asKindOf!kind;
668                 } ();
669                 return null;
670             }
671             // TODO: create a single allocation algorithm
672             else
673             {
674                 import mir.ndslice.fuse: rcfuse;
675 
676                 RCArray!(Slice!(RCI!D, N - 1)) array;
677                 if (auto ret = deserializeValue(data, array, table, tableIndex))
678                     return ret;
679                 () @trusted {
680                     value = array.moveToSlice.rcfuse.asKindOf!kind;
681                 } ();
682                 return null;
683             }
684         }
685         else
686         static if (is(T == RCArray!D, D))
687         {
688             alias E = Unqual!D;
689             if (data.descriptor.type == IonTypeCode.list)
690             {
691                 auto buffer = scopedBuffer!E;
692                 if (auto exception = deserializeListToScopedBuffer(data, buffer, table, tableIndex))
693                     return exception;
694                 auto ar = RCArray!E(buffer.length, false);
695                 () @trusted {
696                     buffer.moveDataAndEmplaceTo(ar[]);
697                 } ();
698                 static if (__traits(compiles, value = move(ar)))
699                     value = move(ar);
700                 else () @trusted {
701                     value = ar.opCast!T;
702                 } ();
703                 return null;
704             }
705             else
706             if (data.descriptor.type == IonTypeCode.null_)
707             {
708                 value = null;
709                 return null;
710             }
711             return IonErrorCode.expectedListValue.ionException;
712         }
713         else
714         static if (hasUDA!(T, serdeProxy))
715         {
716             import mir.conv: to;
717             import core.lifetime: move;
718             static if (hasUDA!(T, serdeScoped))
719                 static if (is(serdeGetProxy!T == C[], C) && is(immutable C == immutable char))
720                     alias impl = deserializeScoped;
721                 else
722                     alias impl = deserializeValue;
723             else
724                 alias impl = deserializeValue;
725 
726             static if (hasUDA!(T, serdeLikeStruct))
727             {
728                 import mir.conv;
729                 if (data.descriptor.type != IonTypeCode.null_ && data.descriptor.type != IonTypeCode.struct_)
730                     return IonErrorCode.expectedIonStructForAnAssociativeArrayDeserialization.ionException;
731                 value = value.init;
732                 if (data.descriptor.L != 0xF)
733                 foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; data.trustedGet!IonStruct)
734                 {
735                     if (error)
736                         return error.ionException;
737                     if (symbolId >= table.length)
738                         return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
739                     import mir.conv: to;
740                     serdeGetProxy!T temporal;
741                     if (auto exception = impl(elem, temporal, table, tableIndex))
742                         return exception;
743                     static if (tableKind == TableKind.immutableRuntime)
744                     {
745                         static if (is(typeof((()@trusted pure {value[table[symbolId]] = move(temporal);}))))
746                             (()@trusted {value[table[symbolId]] = move(temporal);})();
747                         else
748                         {
749                             pragma(msg, "Mir warning: " ~ T.stringof ~
750                                 ".opIndexAssign has to be @safe pure scope");
751                             (()@trusted=>(cast(void delegate() @safe pure) () {value[table[symbolId]] = move(temporal);}))();
752                         }
753                     }
754                     else
755                     {
756                         value[table[symbolId].idup] = move(temporal);
757                     }
758                 }
759             }
760             else
761             static if (hasUDA!(T, serdeLikeList))
762             {
763                 import mir.conv;
764                 if (data.descriptor.type != IonTypeCode.null_ && data.descriptor.type != IonTypeCode.list)
765                     return IonErrorCode.expectedListValue.ionException;
766                 if (data.descriptor.L != 0xF)
767                 foreach (IonErrorCode error, scope IonDescribedValue elem; data.trustedGet!IonList)
768                 {
769                     if (error)
770                         return error.ionException;
771                     import mir.conv: to;
772                     serdeGetProxy!T temporal;
773                     if (auto exception = impl(elem, temporal, table, tableIndex))
774                         return exception;
775                     value.put(move(temporal));
776                 }
777             }
778             else
779             {
780                 serdeGetProxy!T temporal;
781                 static if (is(serdeGetProxy!T == _C[], _C) && is(immutable _C == immutable char))
782                     alias proxyAnnotations = AliasSeq!();
783                 else
784                     alias proxyAnnotations = annotations_;
785 
786                 if (auto exception = impl(data, temporal, table, tableIndex, proxyAnnotations))
787                     return exception;
788 
789                 static if (hasUDA!(T, serdeProxyCast))
790                 {
791                     static if (__traits(compiles, ()@safe{return cast(T)temporal;}))
792                         value = cast(T)temporal;
793                     else
794                     {
795                         pragma(msg, "Mir warning: can't safely cast from "
796                             ~ (const serdeGetProxy!T).stringof
797                             ~ " to "
798                             ~ T.stringof
799                         );
800                         value = ()@trusted{return cast(T)temporal;}();
801                     }
802                 }
803                 else
804                 {
805                     static if (__traits(compiles, ()@safe{return to!T(move(temporal));}))
806                         value = to!T(move(temporal));
807                     else
808                     {
809                         pragma(msg, "Mir warning: can't safely cast from "
810                             ~ (const serdeGetProxy!T).stringof
811                             ~ " to "
812                             ~ T.stringof
813                         );
814                         value = ()@trusted{return to!T(move(temporal));}();
815                     }
816                 }
817             }
818             static if(__traits(hasMember, T, "serdeFinalize"))
819             {
820                 value.serdeFinalize();
821             }
822             return null;
823         }
824         else
825         static if (is(T == enum))
826         {
827             scope const(char)[] ionValue;
828             if (data.descriptor.type == IonTypeCode.symbol)
829             {
830                 size_t id;
831                 if (auto exc = data.trustedGet!IonSymbolID.get(id))
832                     return exc.ionException;
833                 if (id >= table.length)
834                     return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
835 
836                 auto originalId = id;
837                 if (!prepareSymbolId(id, tableIndex))
838                     goto Default;
839 
840                 switch (id)
841                 {
842                     import std.meta: NoDuplicates;
843                     alias Members = NoDuplicates!(EnumMembers!T);
844                     foreach(i, member; Members)
845                     {{
846                         enum keys = serdeGetKeysIn(Members[i]);
847                         static assert (keys.length, "At least one input enum key is required");
848                         static foreach (key; keys)
849                         {
850                             case findKey(symbolTable, key):
851                             value = member;
852                             return null;
853                         }
854                     }}
855                  Default:
856                  default:
857                         static if (hasUDA!(T, serdeIgnoreCase))
858                             ionValue = table[originalId];
859                         else
860                             return IonErrorCode.expectedEnumValue.ionException;
861                 }
862             }
863             else
864             {
865                 import mir.serde: serdeParseEnum;
866                 IonErrorCode error;
867                 ionValue = data.get!(const(char)[])(error);
868                 if (error)
869                     return error.ionException;
870             }
871             if (serdeParseEnum(ionValue, value))
872                 return null;
873             return IonErrorCode.expectedEnumValue.ionException;
874         }
875         else
876         static if (isStdNullable!T && !isAlgebraicAliasThis!T)
877         {
878             // TODO: check that descriptor.type correspond underlaying type
879             if (data.descriptor.L == 0xF)
880             {
881                 value.nullify;
882                 return null;
883             }
884 
885             typeof(value.get) payload;
886             if (auto exception = deserializeValue(data, payload, table, tableIndex, annotations_))
887                 return exception;
888             value = payload;
889             return null;
890         }
891         else
892         {
893             static if (serdeGetAnnotationMembersIn!T.length || Annotations.length)
894             {
895                 static if (!Annotations.length)
896                 {
897                     if (data.descriptor.type != IonTypeCode.annotations)
898                     {
899                         return unqualException(cantDesrializeUnexpectedDescriptorType!T);
900                     }
901 
902                     IonErrorCode _wrapperError;
903                     auto wrapper = data.get!IonAnnotationWrapper(_wrapperError);
904                     if (_wrapperError)
905                         return _wrapperError.ionException;
906                     auto annotations = wrapper.annotations;
907                     (()@trusted {data = wrapper.value;})();
908                 }
909                 else
910                 {
911                     alias annotations = annotations_[0];
912                 }
913 
914                 static foreach (member; serdeGetAnnotationMembersIn!T)
915                 {{
916                     if (annotations.empty)
917                         return IonErrorCode.missingAnnotation.ionException;
918                     for(;;)
919                     {
920                         size_t symbolId;
921                         if (auto error = annotations.pick(symbolId))
922                             return error.ionException;
923                         if (symbolId >= table.length)
924                             return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
925                         static if (is(typeof(__traits(getMember, value, member)) == enum))
926                         {
927                             import mir.serde: serdeParseEnum;
928                             typeof(__traits(getMember, value, member)) memberValue;
929                             if (!serdeParseEnum(table[symbolId], memberValue))
930                                 return IonErrorCode.cantConvertAnnotationToEnum.ionException;
931                             __traits(getMember, value, member) = memberValue;
932                             break;
933                         }
934                         else
935                         static if (__traits(compiles, __traits(getMember, value, member) = table[symbolId]))
936                         {
937                             (() @trusted pure {__traits(getMember, value, member) = table[symbolId];})() ;
938                             break;
939                         }
940                         else
941                         static if (__traits(compiles, __traits(getMember, value, member) = table[symbolId].idup))
942                         {
943                             __traits(getMember, value, member) = table[symbolId].idup;
944                             break;
945                         }
946                         else
947                         {
948                             alias AT = typeof(__traits(getMember, value, member));
949                             static if (!isSomeChar!(ForeachType!AT))
950                             {
951                                 import mir.conv : to;
952                                 static if (__traits(isIntegral, AT))
953                                     __traits(getMember, value, member) ~= table[symbolId].to!(ForeachType!AT);
954                                 else
955                                 static if (tableKind == TableKind.immutableRuntime || is(AT : const(char)[]))
956                                     (()pure @trusted {__traits(getMember, value, member) ~= table[symbolId];})();
957                                 else
958                                     __traits(getMember, value, member) ~= table[symbolId].idup;
959                                 if (annotations.empty)
960                                     break;
961                             }
962                             else
963                             static assert(0, "Can't deserialize annotation member " ~ member ~ " of " ~ T.stringof);
964                         }
965                     }
966                 }}
967 
968                 alias annotations__ = AliasSeq!(annotations);
969             }
970             else
971                 alias annotations__ = annotations_;
972 
973             static if (isAlgebraicAliasThis!T || isAnnotated!T)
974             {
975                 import mir.reflection: hasField;
976                 static if (__traits(getAliasThis, T).length == 1)
977                     enum aliasMember = __traits(getAliasThis, T);
978                 else
979                     enum aliasMember = "value";
980                 static if (hasField!(T, aliasMember))
981                     return deserializeValue(data, __traits(getMember, value, aliasMember), table, tableIndex, annotations__);
982                 else {
983                     typeof(__traits(getMember, value, aliasMember)) temporal;
984                     if (auto exception = deserializeValue(data, temporal, table, tableIndex, annotations__))
985                         return exception;
986                     import core.lifetime: move;
987                     __traits(getMember, value, aliasMember) = move(temporal);
988                     return null;
989                 }
990             }
991             else
992             {
993                 static if (serdeGetAnnotationMembersIn!T.length || Annotations.length)
994                 {
995                     if (!annotations.empty)
996                     {
997                         return unqualException(unusedAnnotation!T);
998                     }
999                 }
1000 
1001                 if (data.descriptor.L == 0xF)
1002                 {
1003                     if (data.descriptor.type != IonTypeCode.struct_ && data.descriptor.type != IonTypeCode.null_)
1004                         goto WrongKindL;
1005                     static if (__traits(compiles, value = null))
1006                     {
1007                         value = null;
1008                         return null; 
1009                     }
1010                     else
1011                     {
1012                         return unqualException(cantConstructNullValueOfType!T);
1013                     }
1014                 }
1015 
1016                 if (data.descriptor.type != IonTypeCode.struct_)
1017                 {
1018                 WrongKindL:
1019                     return unqualException(cantDesrializeUnexpectedDescriptorType!T);
1020                 }
1021 
1022                 static if (is(T == interface) || is(T == class))
1023                 {
1024                     if (value is null)
1025                     {
1026                         static if (is(T == class))
1027                         {
1028                             static if (is(typeof(() @safe pure {return new T();})))
1029                             {
1030                                 value = new T();
1031                             }
1032                             else
1033                             {
1034                                 return unqualException(cantConstructObjectExc!T);
1035                             }
1036                         }
1037                         else
1038                         {
1039                             return unqualException(cantConstructObjectExc!T);
1040                         }
1041                     }
1042                 }
1043 
1044                 SerdeFlags!T requiredFlags;
1045 
1046                 static if (hasUDA!(T, serdeOrderedIn))
1047                 {
1048                     SerdeOrderedDummy!T temporal;
1049                     if (auto exception = deserializeValue(data, temporal, table, tableIndex))
1050                         return exception;
1051                     temporal.serdeFinalizeTarget(value, requiredFlags);
1052                 }
1053                 else
1054                 {
1055                     auto ionValue = data.trustedGet!IonStruct;
1056 
1057                     enum hasUnexpectedKeyHandler = __traits(hasMember, T, "serdeUnexpectedKeyHandler");
1058                     enum hasSerdeIgnoreUnexpectedKeys = hasUDA!(T, serdeIgnoreUnexpectedKeys);
1059 
1060                     import std.meta: staticMap, aliasSeqOf;
1061                     static if (hasUDA!(T, serdeRealOrderedIn))
1062                     {
1063                         static assert (!hasUnexpectedKeyHandler, "@serdeRealOrderedIn aggregate type attribute is not compatible with `serdeUnexpectedKeyHandler` method");
1064                         static assert (!hasSerdeIgnoreUnexpectedKeys, "@serdeRealOrderedIn aggregate type attribute is not compatible with @hasSerdeIgnoreUnexpectedKeys");
1065                         static foreach (member; serdeFinalProxyDeserializableMembers!T)
1066                         {{
1067                             enum keys = serdeGetKeysIn!(__traits(getMember, value, member));
1068                             static if (keys.length)
1069                             {
1070                                 foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; ionValue)
1071                                 {
1072                                     if (error)
1073                                         return error.ionException;
1074                                     prepareSymbolId(symbolId, tableIndex);
1075 
1076                                     switch(symbolId)
1077                                     {
1078                                         static foreach (key; keys)
1079                                         {
1080                                         case findKey(symbolTable, key):
1081                                         }
1082 
1083                                             static if(hasUDA!(T, member, serdeIgnoreIfAggregate))
1084                                             {
1085                                                 alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member));
1086                                                 if (pred(value))
1087                                                 {
1088                                                     __traits(getMember, requiredFlags, member) = true;
1089                                                     goto default;
1090                                                 }
1091                                             }
1092 
1093                                             static if(hasUDA!(T, member, serdeIgnoreInIfAggregate))
1094                                             {
1095                                                 alias pred = serdeGetIgnoreInIfAggregate!(__traits(getMember, value, member));
1096                                                 if (pred(value))
1097                                                 {
1098                                                     __traits(getMember, requiredFlags, member) = true;
1099                                                     goto default;
1100                                                 }
1101                                             }
1102 
1103                                             if (auto mexp = deserializeValueMember!member(elem, value, requiredFlags, table, tableIndex))
1104                                                 return mexp;
1105                                             break;
1106                                         default:
1107                                     }
1108                                 }
1109                             }
1110 
1111                             static if (!hasUDA!(T, member, serdeOptional))
1112                             {
1113                                 static if(hasUDA!(T, member, serdeIgnoreIfAggregate))
1114                                 {
1115                                     alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member));
1116                                     if (!__traits(getMember, requiredFlags, member) && !pred(value))
1117                                         return unqualException(exc!(T, member));
1118                                 }
1119                                 else
1120                                 static if(hasUDA!(T, member, serdeIgnoreInIfAggregate))
1121                                 {
1122                                     alias pred = serdeGetIgnoreInIfAggregate!(__traits(getMember, value, member));
1123                                     if (!__traits(getMember, requiredFlags, member) && !pred(value))
1124                                         return unqualException(exc!(T, member));
1125                                 }
1126                                 else
1127                                 {
1128                                     if (!__traits(getMember, requiredFlags, member))
1129                                         return unqualException(exc!(T, member));
1130                                 }
1131                             }
1132                         }}
1133                  }
1134                     else
1135                     {
1136                         foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; ionValue)
1137                         {
1138                             if (error)
1139                                 return error.ionException;
1140                             auto originalId = symbolId;
1141                             if (!prepareSymbolId(symbolId, tableIndex))
1142                                 goto Default;
1143                             S: switch(symbolId)
1144                             {
1145                                 static foreach (member; serdeFinalProxyDeserializableMembers!T)
1146                                 {{
1147                                     enum keys = serdeGetKeysIn!(T, member);
1148                                     static if (keys.length)
1149                                     {
1150                                         static foreach (key; keys)
1151                                         {
1152                                 case findKey(symbolTable, key):
1153                                         }
1154                                     static if(hasUDA!(T, member, serdeIgnoreInIfAggregate))
1155                                     {
1156                                         alias pred = serdeGetIgnoreInIfAggregate!(__traits(getMember, value, member));
1157                                         if (pred(value))
1158                                         {
1159                                             static if (hasUnexpectedKeyHandler && !hasUDA!(T, member, serdeOptional))
1160                                                 __traits(getMember, requiredFlags, member) = true;
1161                                             goto default;
1162                                         }
1163                                     }
1164                                     static if(hasUDA!(T, member, serdeIgnoreIfAggregate))
1165                                     {
1166                                         alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member));
1167                                         if (pred(value))
1168                                         {
1169                                             static if (hasUnexpectedKeyHandler && !hasUDA!(T, member, serdeOptional))
1170                                                 __traits(getMember, requiredFlags, member) = true;
1171                                             goto default;
1172                                         }
1173                                     }
1174                                     if (auto mexp = deserializeValueMember!member(elem, value, requiredFlags, table, tableIndex))
1175                                         return mexp;
1176                                     break S;
1177                                     }
1178                                 }}
1179                              Default:
1180                                 default:
1181                                     static if (hasDiscriminatedField!T)
1182                                     {
1183                                         if (originalId < table.length && table[originalId] == getUDAs!(T, serdeDiscriminatedField)[0].field)
1184                                         {
1185                                             break;
1186                                         }
1187                                     }
1188 
1189                                     static if (hasUnexpectedKeyHandler)
1190                                         value.serdeUnexpectedKeyHandler(originalId < table.length ? table[originalId] : "<@unknown symbol@>");
1191                                     else
1192                                     static if (!hasSerdeIgnoreUnexpectedKeys)
1193                                         return unqualException(unexpectedKeyWhenDeserializing!T);
1194                             }
1195                         }
1196 
1197                         static foreach (member; __traits(allMembers, SerdeFlags!T))
1198                             static if (!hasUDA!(T, member, serdeOptional))
1199                             {
1200                                 static if(hasUDA!(T, member, serdeIgnoreIfAggregate))
1201                                 {
1202                                     alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member));
1203                                     if (!__traits(getMember, requiredFlags, member) && !pred(value))
1204                                         return unqualException(exc!(T, member));
1205                                 }
1206                                 else
1207                                 static if(hasUDA!(T, member, serdeIgnoreInIfAggregate))
1208                                 {
1209                                     alias pred = serdeGetIgnoreInIfAggregate!(__traits(getMember, value, member));
1210                                     if (!__traits(getMember, requiredFlags, member) && !pred(value))
1211                                         return unqualException(exc!(T, member));
1212                                 }
1213                                 else
1214                                 {
1215                                     if (!__traits(getMember, requiredFlags, member))
1216                                         return unqualException(exc!(T, member));
1217                                 }
1218                             }
1219                     }
1220                 }
1221 
1222                 static if(__traits(hasMember, T, "serdeFinalizeWithFlags"))
1223                 {
1224                     value.serdeFinalizeWithFlags(requiredFlags);
1225                 }
1226                 static if(__traits(hasMember, T, "serdeFinalize"))
1227                 {
1228                     value.serdeFinalize();
1229                 }
1230                 return null;
1231             }
1232         }
1233     }
1234 
1235     IonException deserializeValue(T, Annotations...)(
1236         scope IonDescribedValue data,
1237         return scope ref T value,
1238         scope RuntimeSymbolTable table,
1239         scope const(uint)[] tableIndex,
1240         scope Annotations annotations_,
1241         )
1242         if (!isFirstOrderSerdeType!T && isVariant!T && Annotations.length <= 1)
1243     {
1244         import mir.internal.meta: Contains;
1245         import mir.ndslice.slice: Slice, SliceKind;
1246         import mir.rc.array: RCArray, RCI;
1247         import mir.reflection: isStdNullable;
1248         import mir.string_map : isStringMap;
1249         import std.meta: anySatisfy, Filter, templateAnd, templateNot, templateOr, ApplyRight;
1250         import std.traits: isArray, isSomeString, isAssociativeArray;
1251 
1252 
1253         import mir.lob: Blob, Clob;
1254         import mir.timestamp: Timestamp;
1255 
1256         static if (getAlgebraicAnnotationsOfVariant!T.length)
1257         {
1258             static if (!Annotations.length)
1259             {
1260                 IonAnnotations annotations__;
1261                 if (data.descriptor.type == IonTypeCode.annotations)
1262                 {
1263                     IonErrorCode error;
1264                     auto wrapper = data.get!IonAnnotationWrapper(error);
1265                     if (error)
1266                         return error.ionException;
1267                     () @trusted {
1268                         annotations__ = wrapper.annotations;
1269                         data = wrapper.value;
1270                     } ();
1271                 }
1272                 
1273             }
1274             else
1275             {
1276                 alias annotations__ = annotations_[0];
1277             }
1278 
1279             IonException retNull() @property
1280             {
1281                 return annotations__.empty ? null : IonErrorCode.unusedAnnotations.ionException;
1282             }
1283         }
1284         else
1285         {
1286             IonException retNull;
1287         }
1288 
1289         alias Types = T.AllowedTypes;
1290         alias contains = Contains!Types;
1291 
1292         static if (getAlgebraicAnnotationsOfVariant!T.length)
1293         {
1294             if (!annotations__.empty)
1295             {
1296                 size_t symbolId;
1297                 if (auto error = annotations__.pick(symbolId))
1298                     return error.ionException;
1299 
1300                 auto originalId = symbolId;
1301                 if (!prepareSymbolId(symbolId, tableIndex))
1302                     goto Default;
1303 
1304                 switch (symbolId)
1305                 {
1306                     static foreach (VT; Types)
1307                     static if (serdeHasAlgebraicAnnotation!VT)
1308                     {
1309                         case findKey(symbolTable, serdeGetAlgebraicAnnotation!VT):
1310                         {
1311                             VT object;
1312                             if (auto exception = deserializeValue(data, object, table, tableIndex, annotations__))
1313                                 return exception;
1314                             import core.lifetime: move;
1315                             value = move(object);
1316                             return null;
1317                         }
1318                     }
1319                     Default:
1320                     default:
1321                         static if (__traits(hasMember, T, "serdeUnexpectedAnnotationHandler"))
1322                             value.serdeUnexpectedAnnotationHandler(originalId < table.length ? table[originalId] : "<@unknown symbol@>");
1323                         else
1324                             return unqualException(unexpectedAnnotationWhenDeserializing!T);
1325                 }
1326             }
1327             else
1328             if (data.descriptor.type == IonTypeCode.struct_)
1329             {
1330                 auto dataStruct = data.trustedGet!IonStruct;
1331                 if (dataStruct.walkLength == 1)
1332                 {
1333                     foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; dataStruct)
1334                     {
1335                         if (error)
1336                             return error.ionException;
1337                         auto originalId = symbolId;
1338                         if (prepareSymbolId(symbolId, tableIndex)) switch (symbolId)
1339                         {
1340                             static foreach (VT; Types)
1341                             static if (serdeHasAlgebraicAnnotation!VT)
1342                             {
1343                                 case findKey(symbolTable, serdeGetAlgebraicAnnotation!VT):
1344                                 {
1345                                     VT object;
1346                                     if (auto exception = deserializeValue(elem, object, table, tableIndex, annotations__))
1347                                         return exception;
1348                                     import core.lifetime: move;
1349                                     value = move(object);
1350                                     return null;
1351                                 }
1352                             }
1353                             default:
1354                         }
1355                     }
1356                 }
1357             }
1358         }
1359         static if (contains!IonNull)
1360         {
1361             // TODO: check that descriptor.type correspond underlaying type
1362             if (data.descriptor.L == 0xF)
1363             {
1364                 value = IonNull(data.descriptor.type);
1365                 return retNull;
1366             }
1367         }
1368         else
1369         static if (contains!(typeof(null)))
1370         {
1371             // TODO: check that descriptor.type correspond underlaying type
1372             if (data.descriptor.L == 0xF)
1373             {
1374                 value = null;
1375                 return retNull;
1376             }
1377         }
1378         static if (T.AllowedTypes.length == 2
1379             && (contains!(typeof(null)) || contains!IonNull || is(T.AllowedTypes[0] == void)))
1380         {
1381             T.AllowedTypes[1] payload;
1382             if (auto exception = deserializeValue(data, payload, table, tableIndex))
1383                 return exception;
1384             value = payload;
1385             return retNull;
1386         }
1387         else
1388         switch (data.descriptor.type)
1389         {
1390             // static if (contains!(typeof(null)))
1391             // {
1392             //     case IonTypeCode.null_:
1393             //     {
1394             //         value = null;
1395             //         return retNull;
1396             //     }
1397             // }
1398 
1399             static if (contains!bool)
1400             {
1401                 case IonTypeCode.bool_:
1402                 {
1403                     IonErrorCode error;
1404                     bool boolean = data.get!bool(error);
1405                     if (error)
1406                         return error.ionException;
1407                     value = boolean;
1408                     return retNull;
1409                 }
1410             }
1411 
1412             static if (contains!string)
1413             {
1414                 case IonTypeCode.symbol:
1415                 case IonTypeCode..string:
1416                 {
1417                     string str;
1418                     if (auto exception = deserializeValue(data, str, table, tableIndex))
1419                         return exception;
1420                     value = str;
1421                     return retNull;
1422                 }
1423             }
1424             else
1425             static if (Filter!(isSmallString, Types).length)
1426             {
1427                 case IonTypeCode.symbol:
1428                 case IonTypeCode..string:
1429                 {
1430                     Filter!(isSmallString, Types)[$ - 1] str; // pick the largest one
1431                     if (auto exception = deserializeValue(data, str, table, tableIndex))
1432                         return exception;
1433                     value = str;
1434                     return retNull;
1435                 }
1436             }
1437 
1438             static if (contains!long)
1439             {
1440                 case IonTypeCode.nInt:
1441                 case IonTypeCode.uInt:
1442                 {
1443                     long number;
1444                     if (auto exception = deserializeValue_(data, number))
1445                         return exception;
1446                     value = number;
1447                     return retNull;
1448                 }
1449             }
1450 
1451             static if (contains!double)
1452             {
1453                 static if (!contains!long)
1454                 {
1455                     case IonTypeCode.nInt:
1456                     case IonTypeCode.uInt:
1457                 }
1458                 case IonTypeCode.float_:
1459                 case IonTypeCode.decimal:
1460                 {
1461                     double number;
1462                     if (auto exception = deserializeValue_(data, number))
1463                         return exception;
1464                     value = number;
1465                     return retNull;
1466                 }
1467             }
1468 
1469             static if (contains!Timestamp)
1470             {
1471                 case IonTypeCode.timestamp:
1472                 {
1473                     Timestamp timestamp;
1474                     if (auto error = data.trustedGet!IonTimestamp.get(timestamp))
1475                         return error.ionException;
1476                     value = timestamp;
1477                     return retNull;
1478                 }
1479             }
1480 
1481             static if (contains!Blob)
1482             {
1483                 case IonTypeCode.blob:
1484                 {
1485                     auto blob = data.trustedGet!Blob;
1486                     value = Blob(blob.data.dup);
1487                     return retNull;
1488                 }
1489             }
1490 
1491             static if (contains!Clob)
1492             {
1493                 case IonTypeCode.clob:
1494                 {
1495                     auto clob = data.trustedGet!Clob;
1496                     value = Clob(clob.data.dup);
1497                     return retNull;
1498                 }
1499             }
1500 
1501             static if (anySatisfy!(templateOr!(templateAnd!(isArray, templateNot!isSomeString), isTuple), Types))
1502             {
1503                 case IonTypeCode.list:
1504                 {
1505                     alias ArrayTypes = Filter!(templateOr!(templateAnd!(isArray, templateNot!isSomeString), isTuple), Types);
1506                     static assert(ArrayTypes.length == 1, ArrayTypes.stringof);
1507                     value = ArrayTypes[0].init;
1508                     return deserializeValue(data, value.trustedGet!(ArrayTypes[0]), table, tableIndex, annotations_);
1509                 }
1510             }
1511 
1512             static if (anySatisfy!(templateOr!(isStringMap, isAssociativeArray, hasLikeStruct, hasFallbackStruct, hasDiscriminatedField), Types))
1513             {
1514                 case IonTypeCode.struct_:
1515                 {
1516                     static if (anySatisfy!(isStringMap, Types))
1517                     {
1518                         alias isMapType = isStringMap;
1519                     }
1520                     else
1521                     static if (anySatisfy!(isAssociativeArray, Types))
1522                     {
1523                         alias isMapType = isAssociativeArray;
1524                     }
1525                     else
1526                     static if (anySatisfy!(hasLikeStruct, Types))
1527                     {
1528                         alias isMapType = hasLikeStruct;
1529                     }
1530                     else
1531                     static if (anySatisfy!(hasFallbackStruct, Types))
1532                     {
1533                         alias isMapType = hasFallbackStruct;
1534                     }
1535                     else
1536                     static if (anySatisfy!(hasDiscriminatedField, Types))
1537                     {
1538                         alias isMapType = hasDiscriminatedField;
1539                     }
1540                     else
1541                     {
1542                         static assert(0);
1543                     }
1544 
1545                     alias DiscriminatedFieldTypes = Filter!(hasDiscriminatedField, Types);
1546                     static if (DiscriminatedFieldTypes.length)
1547                     {
1548                         enum discriminatedField = getUDAs!(DiscriminatedFieldTypes[0], serdeDiscriminatedField)[0].field;
1549                         foreach (DFT; DiscriminatedFieldTypes[1 .. $])
1550                         {{
1551                             enum df = getUDAs!(DFT, serdeDiscriminatedField)[0].field;
1552                             static assert (df == discriminatedField, "Discriminated field doesn't match: " ~ discriminatedField ~ " and " ~ df);
1553                         }}
1554 
1555                         foreach (IonErrorCode error, size_t symbolId, scope IonDescribedValue elem; data.trustedGet!IonStruct)
1556                         {
1557                             if (error)
1558                                 return error.ionException;
1559                             if (symbolId >= table.length)
1560                                 return IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable.ionException;
1561                             if (table[symbolId] == discriminatedField)
1562                             {
1563                                 const(char)[] tag;
1564                                 if (auto exception = deserializeScoped(elem, tag, table, tableIndex))
1565                                     return exception;
1566                                 switch (tag)
1567                                 {
1568                                     foreach (DFT; DiscriminatedFieldTypes)
1569                                     {
1570                                         case getUDAs!(DFT, serdeDiscriminatedField)[0].tag: {
1571                                             DFT object;
1572                                             if (auto exception = deserializeValue(data, object, table, tableIndex, annotations_))
1573                                                 return exception;
1574                                             import core.lifetime: move;
1575                                             value = move(object);
1576                                             return retNull;
1577                                         }
1578                                     }
1579                                     default:
1580                                 }
1581                             }
1582                         }
1583                     }
1584 
1585                     static if (__traits(isSame, isMapType, hasDiscriminatedField))
1586                     {
1587                         goto default;
1588                     }
1589                     else
1590                     {
1591                         alias AATypes = Filter!(isMapType, Types);
1592                         static assert(AATypes.length == 1, AATypes.stringof);
1593                         AATypes[0] object;
1594                         if (auto exception = deserializeValue(data, object, table, tableIndex, annotations_))
1595                             return exception;
1596                         import core.lifetime: move;
1597                         value = move(object);
1598                         return retNull;
1599                     }
1600                 }
1601             }
1602 
1603             static if (anySatisfy!(isAnnotated, Types))
1604             {
1605                 case IonTypeCode.annotations:
1606                 {
1607                     alias ATypes = Filter!(isAnnotated, Types);
1608                     static assert(ATypes.length == 1, ATypes.stringof);
1609                     ATypes[0] object;
1610                     if (auto exception = deserializeValue(data, object, table, tableIndex, annotations_))
1611                         return exception;
1612                     import core.lifetime: move;
1613                     value = move(object);
1614                     return retNull;
1615                 }
1616             }
1617 
1618 
1619             default:
1620                 return unqualException(unexpectedIonTypeCodeFor!T);
1621         }
1622 
1623     }
1624 
1625     ///
1626     alias deserializeValue = .deserializeValue_;
1627 }
1628 
1629 version(mir_ion_test)
1630 unittest
1631 {
1632     import mir.algebraic_alias.json : JsonAlgebraic;
1633     import mir.deser.json : deserializeJson;
1634     auto v = deserializeJson!JsonAlgebraic(`{"a":[1,"world",false,null]}`);
1635 }
1636 
1637 ///
1638 @safe pure version(none)
1639 version(mir_ion_test) unittest
1640 {
1641     import mir.ion.symbol_table;
1642     import mir.ion.value;
1643     import mir.ion.exception;
1644     import mir.small_array;
1645     import mir.small_string;
1646 
1647     static struct Book
1648     {
1649         string title;
1650         bool wouldRecommend;
1651         string description;
1652         uint numberOfNovellas;
1653         double price;
1654         float weight;
1655         string[] tags;
1656     }
1657 
1658     static immutable symbolTable = ["title", "wouldRecommend", "description", "numberOfNovellas", "price", "weight", "tags"];
1659     static immutable binaryData = cast(immutable ubyte[]) "\xde\xc9\x8a\x8e\x92A Hero of Our Time\x8b\x11\x8c\x0f\x8d!\x05\x8eS\xc2\x03\x1f\x8fH@\x1b\x85\x1e\xb8Q\xeb\x85\x90\xbe\x9b\x87russian\x85novel\x8c19th century";
1660 
1661     auto data = IonValue(binaryData).describe;
1662     
1663     Book book;
1664 
1665     if (auto exception = deserializeValue!(IonSystemSymbolTable_v1 ~ symbolTable, TableKind.immutableRuntime, false)(data, book))
1666         throw exception;
1667 
1668     assert(book.description.length == 0);
1669     assert(book.numberOfNovellas == 5);
1670     assert(book.price == 7.99);
1671     assert(book.tags.length == 3);
1672     assert(book.tags[0] == "russian");
1673     assert(book.tags[1] == "novel");
1674     assert(book.tags[2] == "19th century");
1675     assert(book.title == "A Hero of Our Time");
1676     assert(book.weight == 6.88f);
1677     assert(book.wouldRecommend);
1678 }
1679 
1680 ///
1681 version(mir_ion_test) unittest
1682 {
1683     import mir.deser.json;
1684     import std.uuid;
1685 
1686     static struct S
1687     {
1688         @serdeScoped
1689         @serdeProxy!string
1690         UUID id;
1691     }
1692     assert(`{"id":"8AB3060E-2cba-4f23-b74c-b52db3bdfb46"}`.deserializeJson!S.id
1693                 == UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"));
1694 }
1695 
1696 /// Mir types
1697 version(mir_ion_test) unittest
1698 {
1699     import mir.bignum.integer;
1700     import mir.date;
1701     import mir.deser.json: deserializeJson;
1702     assert(`"2021-04-24"`.deserializeJson!Date == Date(2021, 4, 24));
1703     assert(`123`.deserializeJson!(BigInt!2) == BigInt!2(123));
1704 }
1705 
1706 /// Mir types
1707 @safe pure @nogc
1708 version(mir_ion_test) unittest
1709 {
1710     static struct S
1711     {
1712         @serdeIgnoreIn
1713         bool set;
1714         @serdeScoped
1715         @property auto a(scope int[] a) @safe
1716         {
1717             static immutable d = [1, 2, 3];
1718             set = a == d;
1719         }
1720     }
1721     import mir.deser.json: deserializeJson;
1722     auto s = `{"a":[1, 2, 3]}`.deserializeJson!S;
1723     assert(s.set);
1724 }
1725 
1726 ///
1727 @safe pure //@nogc
1728 version(mir_ion_test) unittest
1729 {
1730     enum Kind { request, cancel }
1731 
1732     @serdeRealOrderedIn
1733     static struct S
1734     {
1735         Kind kind;
1736 
1737         @serdeIgnoreInIfAggregate!((ref a) => a.kind == Kind.cancel)
1738         @serdeIgnoreOutIfAggregate!((ref a) => a.kind == Kind.cancel)
1739         int number;
1740     }
1741 
1742     import mir.deser.json: deserializeJson;
1743     import mir.ser.json: serializeJson;
1744     assert(`{"number":3, "kind":"cancel"}`.deserializeJson!S.kind == Kind.cancel);
1745     assert(`{"number":3, "kind":"cancel"}`.deserializeJson!S.number == 0);
1746     assert(`{"number":3, "kind":"request"}`.deserializeJson!S.number == 3);
1747     assert(`{"kind":"request","number":3}`.deserializeJson!S.number == 3);
1748     assert(S(Kind.cancel, 4).serializeJson == `{"kind":"cancel"}`);
1749     assert(S(Kind.request, 4).serializeJson == `{"kind":"request","number":4}`);
1750 }
1751 
1752 ///
1753 @safe pure //@nogc
1754 version(mir_ion_test) unittest
1755 {
1756     enum Kind { request, cancel }
1757 
1758     @serdeRealOrderedIn
1759     static struct S
1760     {
1761         Kind kind;
1762 
1763         @serdeIgnoreIfAggregate!((ref a) => a.kind == Kind.cancel)
1764         int number;
1765     }
1766 
1767     import mir.deser.json: deserializeJson;
1768     import mir.ser.json: serializeJson;
1769     assert(`{"kind":"cancel"}`.deserializeJson!S.kind == Kind.cancel);
1770     assert(`{"kind":"cancel","number":3}`.deserializeJson!S.number == 0); // ignores number
1771     assert(`{"kind":"request","number":3}`.deserializeJson!S.number == 3);
1772     assert(S(Kind.cancel, 4).serializeJson == `{"kind":"cancel"}`);
1773     assert(S(Kind.request, 4).serializeJson == `{"kind":"request","number":4}`);
1774 }
1775 
1776 version(mir_ion_test) unittest
1777 {
1778     import mir.deser.json;
1779     import mir.algebraic : Nullable;
1780     import mir.ion.value : IonDescribedValue;
1781     import mir.ion.exception : IonException;
1782     import mir.deser.ion : deserializeIon;
1783 
1784     static struct Q
1785     {
1786         int i;
1787         IonException deserializeFromIon(scope const char[][] symbolTable, scope IonDescribedValue value) scope @safe pure @nogc
1788         {
1789             i = deserializeIon!int(symbolTable, value);
1790             return null;
1791         }
1792     }
1793 
1794     // works
1795     // Q s = deserializeJson!Q(`5`);
1796 
1797     static struct T
1798     {
1799         Nullable!Q test;
1800     }
1801 
1802     // does not work
1803     // ../subprojects/mir-core/source/mir/algebraic.d(2883): [unittest] Null Algebraic!(typeof(null), S)
1804     // core.exception.AssertError@../subprojects/mir-core/source/mir/algebraic.d(2883): Null Algebraic!(typeof(null), S)
1805     T t = `{ "test": 5 }`.deserializeJson!T;
1806     assert (!t.test.isNull);
1807 }