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 }