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 }