1 /++ 2 This implements common de/serialization routines. 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 6 Authors: Ilia Ki 7 8 Macros: 9 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 10 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 11 +/ 12 module mir.serde; 13 14 import mir.functional: naryFun; 15 import mir.reflection; 16 import std.meta: AliasSeq; 17 import std.traits: TemplateArgsOf, EnumMembers, isAggregateType; 18 import mir.internal.meta: hasUDA; 19 import mir.reflection: getUDA; 20 21 version (D_Exceptions) 22 { 23 /++ 24 Serde Exception 25 +/ 26 class SerdeException : Exception 27 { 28 /// 29 this( 30 string msg, 31 string file = __FILE__, 32 size_t line = __LINE__, 33 Throwable next = null) pure nothrow @nogc @safe 34 { 35 super(msg, file, line, next); 36 } 37 38 /// 39 this( 40 string msg, 41 Throwable next, 42 string file = __FILE__, 43 size_t line = __LINE__, 44 ) pure nothrow @nogc @safe 45 { 46 this(msg, file, line, next); 47 } 48 49 SerdeException toMutable() @trusted pure nothrow @nogc const 50 { 51 return cast() this; 52 } 53 54 alias toMutable this; 55 } 56 57 /++ 58 Serde Exception with formatting support 59 +/ 60 class SerdeMirException : SerdeException 61 { 62 import mir.exception: MirThrowableImpl, mirExceptionInitilizePayloadImpl; 63 64 enum maxMsgLen = 447; 65 66 /// 67 mixin MirThrowableImpl; 68 } 69 } 70 71 /++ 72 Constructs annotated type. 73 +/ 74 template SerdeAnnotated(T, string annotation) 75 { 76 /// 77 @serdeAlgebraicAnnotation(annotation) 78 @serdeProxy!T 79 struct SerdeAnnotated 80 { 81 /// 82 T value; 83 /// 84 alias value this; 85 } 86 } 87 88 /++ 89 Helper enumeration for for serializer . 90 Use negative `int` values for user defined targets. 91 +/ 92 enum SerdeTarget : int 93 { 94 /// 95 ion, 96 /// 97 json, 98 /// 99 cbor, 100 /// 101 msgpack, 102 /// 103 yaml, 104 /// 105 csv, 106 /// 107 excel, 108 /// 109 bloomberg, 110 /// 111 typedJson, 112 } 113 114 /++ 115 Attribute for key overloading during Serialization and Deserialization. 116 The first argument overloads the key value during serialization unless `serdeKeyOut` is given. 117 +/ 118 struct serdeKeys 119 { 120 /// 121 immutable(string)[] keys; 122 123 @trusted pure nothrow @nogc: 124 /// 125 this(immutable(string)[] keys...) { this.keys = keys; } 126 } 127 128 /++ 129 Attribute for key overloading during serialization. 130 +/ 131 struct serdeKeyOut 132 { 133 /// 134 string key; 135 136 @safe pure nothrow @nogc: 137 /// 138 this(string key) { this.key = key; } 139 } 140 141 /++ 142 The attribute should be used as a hint for scripting languages to register type deserializer in the type system. 143 144 The attribute should be applied to a type definition. 145 +/ 146 enum serdeRegister; 147 148 /++ 149 The attribute can be applied to a string-like member that should be de/serialized as an annotation / attribute. 150 151 Also, the attribute can be applied on a type to denote that the type should be used to de/serialize annotated value. 152 153 This feature is used in $(MIR_PACKAGE mir-ion). 154 +/ 155 enum serdeAnnotation; 156 157 /++ 158 Checks if the type marked with $(LREF serdeAnnotation). 159 +/ 160 template isAnnotated(T) 161 { 162 import mir.serde: serdeAnnotation; 163 static if (is(T == enum) || isAggregateType!T) { 164 enum isAnnotated = hasUDA!(T, serdeAnnotation); 165 static if (isAnnotated) 166 static assert(__traits(getAliasThis, T).length == 1 || __traits(hasMember, T, "value"), "@serdeAnnotation " ~ T.stringof ~" requires alias this member or `value` member."); 167 } 168 else 169 enum isAnnotated = false; 170 } 171 172 private template serdeIsAnnotationMemberIn(T) 173 { 174 enum bool serdeIsAnnotationMemberIn(string member) 175 = hasUDA!(T, member, serdeAnnotation) 176 && !hasUDA!(T, member, serdeIgnore) 177 && !hasUDA!(T, member, serdeIgnoreIn); 178 } 179 180 /++ 181 +/ 182 template serdeGetAnnotationMembersIn(T) 183 { 184 import std.meta: aliasSeqOf, Filter; 185 static if (isAggregateType!T) 186 enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(DeserializableMembers!T))]; 187 else 188 enum string[] serdeGetAnnotationMembersIn = null; 189 } 190 191 192 /// 193 version(mir_test) unittest 194 { 195 struct S 196 { 197 double data; 198 199 @serdeAnnotation 200 string a; 201 @serdeAnnotation @serdeIgnoreIn 202 string b; 203 @serdeAnnotation @serdeIgnoreOut 204 string c; 205 @serdeAnnotation @serdeIgnore 206 string d; 207 } 208 209 static assert(serdeGetAnnotationMembersIn!int == []); 210 static assert(serdeGetAnnotationMembersIn!S == ["a", "c"]); 211 } 212 213 private template serdeIsAnnotationMemberOut(T) 214 { 215 enum bool serdeIsAnnotationMemberOut(string member) 216 = hasUDA!(T, member, serdeAnnotation) 217 && !hasUDA!(T, member, serdeIgnore) 218 && !hasUDA!(T, member, serdeIgnoreOut); 219 } 220 221 /++ 222 +/ 223 template serdeGetAnnotationMembersOut(T) 224 { 225 import std.meta: aliasSeqOf, Filter; 226 import mir.ndslice.topology; 227 static if (isAggregateType!T) 228 enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(SerializableMembers!T))]; 229 else 230 enum string[] serdeGetAnnotationMembersOut = null; 231 } 232 233 /// 234 version(mir_test) unittest 235 { 236 struct S 237 { 238 double data; 239 240 @serdeAnnotation 241 string a; 242 @serdeAnnotation @serdeIgnoreIn 243 string b; 244 @serdeAnnotation @serdeIgnoreOut 245 string c; 246 @serdeAnnotation @serdeIgnore 247 string d; 248 @serdeAnnotation enum string e = "e"; 249 static @serdeAnnotation string f() @safe pure nothrow @nogc @property { 250 return "f"; 251 } 252 } 253 254 static assert(serdeGetAnnotationMembersOut!int == []); 255 static assert(serdeGetAnnotationMembersOut!S == ["a", "b", "f"]); 256 } 257 258 /++ 259 An annotation / attribute for algebraic types deserialization. 260 261 This feature is used in $(MIR_PACKAGE mir-ion) for $(GMREF mir-core, mir,algebraic). 262 +/ 263 struct serdeAlgebraicAnnotation 264 { 265 /// 266 string annotation; 267 268 @safe pure nothrow @nogc: 269 /// 270 this(string annotation) { this.annotation = annotation; } 271 } 272 273 /++ 274 +/ 275 template serdeHasAlgebraicAnnotation(T) 276 { 277 static if (isAggregateType!T || is(T == enum)) 278 { 279 static if (hasUDA!(T, serdeAlgebraicAnnotation)) 280 { 281 enum serdeHasAlgebraicAnnotation = true; 282 } 283 else 284 { 285 enum serdeHasAlgebraicAnnotation = false; 286 } 287 } 288 else 289 { 290 enum serdeHasAlgebraicAnnotation = false; 291 } 292 } 293 294 /++ 295 +/ 296 template serdeGetAlgebraicAnnotation(T) 297 { 298 static if (hasUDA!(T, serdeAlgebraicAnnotation)) 299 { 300 enum string serdeGetAlgebraicAnnotation = getUDA!(T, serdeAlgebraicAnnotation).annotation; 301 } 302 else 303 { 304 private __gshared T* aggregate; 305 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 306 enum serdeGetAlgebraicAnnotation = .serdeGetAlgebraicAnnotation!A; 307 } 308 } 309 310 /++ 311 User defined attribute used to attach a function that returns a deserialization delegate. 312 313 The attribute is usefull for scripting languages and dynamic algebraic types. 314 +/ 315 template serdeDynamicAlgebraic(alias getAlgebraicDeserializerByAnnotation) 316 { 317 enum serdeDynamicAlgebraic; 318 } 319 320 /// 321 version(mir_test) 322 unittest 323 { 324 static struct _global 325 { 326 alias Deserializer = S delegate(string s, ubyte[] data) @safe pure; 327 Deserializer getDeserializer(string name) { return map[name]; } 328 Deserializer[string] map; 329 330 @serdeDynamicAlgebraic!getDeserializer 331 struct S {} 332 333 static assert(serdeIsDynamicAlgebraic!S); 334 static assert(__traits(isSame, serdeGetAlgebraicDeserializer!S, getDeserializer)); 335 } 336 } 337 338 /++ 339 +/ 340 template serdeIsDynamicAlgebraic(T) 341 { 342 static if (isAggregateType!T) 343 { 344 static if (hasUDA!(T, serdeDynamicAlgebraic)) 345 { 346 enum serdeIsDynamicAlgebraic = true; 347 } 348 else 349 static if (__traits(getAliasThis, T).length) 350 { 351 private __gshared T* aggregate; 352 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 353 enum serdeIsDynamicAlgebraic = .serdeIsDynamicAlgebraic!A; 354 } 355 else 356 { 357 enum serdeIsDynamicAlgebraic = false; 358 } 359 } 360 else 361 { 362 enum serdeIsDynamicAlgebraic = false; 363 } 364 } 365 366 /++ 367 +/ 368 template serdeGetAlgebraicDeserializer(T) 369 { 370 static if (hasUDA!(T, serdeDynamicAlgebraic)) 371 { 372 alias serdeGetAlgebraicDeserializer = TemplateArgsOf!(getUDA!(T, serdeDynamicAlgebraic))[0]; 373 } 374 else 375 { 376 private __gshared T* aggregate; 377 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 378 alias serdeGetAlgebraicDeserializer = .serdeGetAlgebraicDeserializer!A; 379 } 380 } 381 382 /++ 383 Returns: 384 immutable array of the input keys for the symbol or enum value 385 +/ 386 template serdeGetKeysIn(alias symbol) 387 { 388 static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn)) 389 enum immutable(string)[] serdeGetKeysIn = null; 390 else 391 static if (hasUDA!(symbol, serdeKeys)) 392 enum immutable(string)[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys; 393 else 394 enum immutable(string)[] serdeGetKeysIn = [__traits(identifier, symbol)]; 395 } 396 397 // ditto 398 template serdeGetKeysIn(T, string member) 399 { 400 static if (hasUDA!(T, member, serdeAnnotation) || hasUDA!(T, member, serdeIgnore) || hasUDA!(T, member, serdeIgnoreIn)) 401 enum immutable(string)[] serdeGetKeysIn = null; 402 else 403 static if (hasUDA!(T, member, serdeKeys)) 404 enum immutable(string)[] serdeGetKeysIn = getUDA!(T, member, serdeKeys).keys; 405 else 406 enum immutable(string)[] serdeGetKeysIn = [member]; 407 } 408 409 /// ditto 410 immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc 411 if (is(T == enum)) 412 { 413 foreach (i, member; EnumMembers!T) 414 {{ 415 alias all = __traits(getAttributes, EnumMembers!T[i]); 416 }} 417 418 import std.meta: staticMap; 419 static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)]; 420 import mir.enums: getEnumIndex; 421 uint index = void; 422 if (getEnumIndex(value, index)) 423 return ret[index]; 424 assert(0); 425 } 426 427 /// 428 version(mir_test) unittest 429 { 430 struct S 431 { 432 int f; 433 434 @serdeKeys("D", "t") 435 int d; 436 437 @serdeIgnore 438 int i; 439 440 @serdeIgnoreIn 441 int ii; 442 443 @serdeIgnoreOut 444 int io; 445 446 void p(int) @property {} 447 } 448 449 static assert(serdeGetKeysIn!(S.f) == ["f"]); 450 static assert(serdeGetKeysIn!(S.d) == ["D", "t"]); 451 static assert(serdeGetKeysIn!(S.i) == null); 452 static assert(serdeGetKeysIn!(S.ii) == null); 453 static assert(serdeGetKeysIn!(S.io) == ["io"]); 454 static assert(serdeGetKeysIn!(S.p) == ["p"]); 455 } 456 457 /// 458 version(mir_test) unittest 459 { 460 enum E 461 { 462 @serdeKeys("A", "alpha") 463 a, 464 @serdeKeys("B", "beta") 465 b, 466 c, 467 } 468 469 static assert (serdeGetKeysIn(E.a) == ["A", "alpha"], serdeGetKeysIn(E.a)); 470 static assert (serdeGetKeysIn(E.c) == ["c"]); 471 } 472 473 /++ 474 Returns: 475 output key for the symbol or enum value 476 +/ 477 template serdeGetKeyOut(alias symbol) 478 { 479 static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut)) 480 enum string serdeGetKeyOut = null; 481 else 482 static if (hasUDA!(symbol, serdeKeyOut)) 483 enum string serdeGetKeyOut = getUDA!(symbol, serdeKeyOut).key; 484 else 485 static if (hasUDA!(symbol, serdeKeys)) 486 enum string serdeGetKeyOut = getUDA!(symbol, serdeKeys).keys[0]; 487 else 488 enum string serdeGetKeyOut = __traits(identifier, symbol); 489 } 490 491 /// ditto 492 template serdeGetKeyOut(T, string member) 493 { 494 static if (hasUDA!(T, member, serdeAnnotation) || hasUDA!(T, member, serdeIgnore) || hasUDA!(T, member, serdeIgnoreOut)) 495 enum string serdeGetKeyOut = null; 496 else 497 static if (hasUDA!(T, member, serdeKeyOut)) 498 enum string serdeGetKeyOut = getUDA!(T, member, serdeKeyOut).key; 499 else 500 static if (hasUDA!(T, member, serdeKeys)) 501 enum string serdeGetKeyOut = getUDA!(T, member, serdeKeys).keys[0]; 502 else 503 enum string serdeGetKeyOut = member; 504 } 505 506 ///ditto 507 @safe pure nothrow @nogc 508 string serdeGetKeyOut(T)(const T value) 509 if (is(T == enum)) 510 { 511 foreach (i, member; EnumMembers!T) 512 {{ 513 alias all = __traits(getAttributes, EnumMembers!T[i]); 514 }} 515 516 import std.meta: staticMap; 517 import mir.enums: getEnumIndex; 518 static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)]; 519 uint index = void; 520 if (getEnumIndex(value, index)) 521 return ret[index]; 522 assert(0); 523 } 524 525 /// 526 version(mir_test) unittest 527 { 528 struct S 529 { 530 int f; 531 532 @serdeKeys("D", "t") 533 int d; 534 535 @serdeIgnore 536 int i; 537 538 @serdeIgnoreIn 539 int ii; 540 541 @serdeIgnoreOut 542 int io; 543 544 @serdeKeys("P") 545 @serdeKeyOut("") 546 void p(int) @property {} 547 } 548 549 static assert(serdeGetKeyOut!(S.f) == "f"); 550 static assert(serdeGetKeyOut!(S.d) == "D"); 551 static assert(serdeGetKeyOut!(S.i) is null); 552 static assert(serdeGetKeyOut!(S.ii) == "ii"); 553 static assert(serdeGetKeyOut!(S.io) is null); 554 static assert(serdeGetKeyOut!(S.p) !is null); 555 static assert(serdeGetKeyOut!(S.p) == ""); 556 } 557 558 /// 559 version(mir_test) unittest 560 { 561 enum E 562 { 563 @serdeKeys("A", "alpha") 564 a, 565 @serdeKeys("B", "beta") 566 @serdeKeyOut("o") 567 b, 568 c, 569 } 570 571 static assert (serdeGetKeyOut(E.a) == "A"); 572 static assert (serdeGetKeyOut(E.b) == "o"); 573 static assert (serdeGetKeyOut(E.c) == "c"); 574 } 575 576 /++ 577 Attribute used to ignore unexpected keys during an aggregate type deserialization. 578 +/ 579 enum serdeIgnoreUnexpectedKeys; 580 581 /++ 582 Attribute to ignore field. 583 584 See_also: $(LREF serdeIgnoreIn) $(LREF serdeIgnoreOut) 585 +/ 586 enum serdeIgnore; 587 588 /++ 589 Attribute to ignore field during deserialization. 590 591 See_also: $(LREF serdeIgnoreInIfAggregate) 592 +/ 593 enum serdeIgnoreIn; 594 595 /++ 596 Attribute to ignore field during serialization. 597 +/ 598 enum serdeIgnoreOut; 599 600 /++ 601 Attribute to ignore a field during deserialization when equals to its default value. 602 Do not use it on void initialized fields or aggregates with void initialized fields, recursively. 603 +/ 604 enum serdeIgnoreDefault; 605 606 /// 607 version(mir_test) unittest 608 { 609 struct S 610 { 611 @serdeIgnoreDefault 612 double d = 0; // skips field if 0 during deserialization 613 } 614 615 616 static assert(hasUDA!(S.d, serdeIgnoreDefault)); 617 } 618 619 /++ 620 +/ 621 622 /++ 623 Serialization proxy. 624 +/ 625 struct serdeProxy(T); 626 627 /// 628 version(mir_test) unittest 629 { 630 import mir.small_string; 631 632 struct S 633 { 634 @serdeProxy!(SmallString!32) 635 double d; 636 } 637 638 639 static assert(hasUDA!(S.d, serdeProxy)); 640 static assert(hasUDA!(S.d, serdeProxy!(SmallString!32))); 641 static assert(is(serdeGetProxy!(S.d) == SmallString!32)); 642 } 643 644 /++ 645 +/ 646 alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0]; 647 /// ditto 648 alias serdeGetProxy(T, string member) = TemplateArgsOf!(getUDA!(T, member, serdeProxy))[0]; 649 650 /// Can be applied to @serdeProxy types to make (de)serialization use 651 /// underlying type through casting. Useful for enums. 652 enum serdeProxyCast; 653 654 /// Equivalent to @serdeProxy!T @serdeProxyCast 655 alias serdeEnumProxy(T) = AliasSeq!(serdeProxy!T, serdeProxyCast); 656 657 /++ 658 Attributes to conditional ignore field during serialization. 659 660 The predicate should be aplied to the member, to the aggregate type. 661 662 See_also: $(LREF serdeIgnoreOutIfAggregate) 663 +/ 664 struct serdeIgnoreOutIf(alias pred); 665 666 /++ 667 +/ 668 alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]); 669 /// ditto 670 alias serdeGetIgnoreOutIf(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreOutIf))[0]); 671 672 /++ 673 Attributes to conditional ignore field during serialization. 674 675 The predicate should be aplied to the aggregate value, not to the member. 676 677 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIf), $(LREF serdeIgnoreInIfAggregate) 678 +/ 679 struct serdeIgnoreOutIfAggregate(alias pred); 680 681 /++ 682 +/ 683 alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIfAggregate))[0]); 684 /// ditto 685 alias serdeGetIgnoreOutIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreOutIfAggregate))[0]); 686 687 /++ 688 Attributes to conditional ignore field during deserialization. 689 690 The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate. 691 692 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreIn) 693 +/ 694 struct serdeIgnoreInIfAggregate(alias pred); 695 696 /++ 697 +/ 698 alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreInIfAggregate))[0]); 699 /// ditto 700 alias serdeGetIgnoreInIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreInIfAggregate))[0]); 701 702 /++ 703 Attributes to conditional ignore field during serialization and deserialization. 704 705 The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate. 706 707 The predicate should be aplied to the aggregate value, not to the member. 708 709 See_also: $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreInIfAggregate) $ $(LREF serdeIgnore) 710 +/ 711 struct serdeIgnoreIfAggregate(alias pred); 712 713 /++ 714 +/ 715 alias serdeGetIgnoreIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreIfAggregate))[0]); 716 /// ditto 717 alias serdeGetIgnoreIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreIfAggregate))[0]); 718 719 /++ 720 Allows to use flexible deserialization rules such as conversion from input string to numeric types. 721 +/ 722 enum serdeFlexible; 723 724 /++ 725 Allows serialize / deserialize fields like arrays. 726 727 A range or a container should be iterable for serialization. 728 Following code should compile: 729 ------ 730 foreach(ref value; yourRangeOrContainer) 731 { 732 ... 733 } 734 ------ 735 736 `put(value)` method is used for deserialization. 737 738 See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn) 739 +/ 740 enum serdeLikeList; 741 742 /++ 743 Allows serialize / deserialize fields like objects. 744 745 Object should have `opApply` method to allow serialization. 746 Following code should compile: 747 ------ 748 foreach(key, value; yourObject) 749 { 750 ... 751 } 752 ------ 753 Object should have only one `opApply` method with 2 argument to allow automatic value type deduction. 754 755 `opIndexAssign` or `opIndex` is used for deserialization to support required syntax: 756 ----- 757 yourObject["key"] = value; 758 ----- 759 Multiple value types is supported for deserialization. 760 761 See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn) 762 +/ 763 enum serdeLikeStruct; 764 765 /++ 766 The attribute is used for algebraic deserialization for types like `Variant!(string, S)` 767 `@serdeFallbackStruct struct S {}` 768 +/ 769 enum serdeFallbackStruct; 770 771 /++ 772 Force serialize / deserialize on fields instead of Range API. 773 +/ 774 enum serdeFields; 775 776 /++ 777 Ignore keys for object and enum members. 778 Should be applied to members or enum type itself. 779 +/ 780 enum serdeIgnoreCase; 781 782 /// 783 bool hasSerdeIgnoreCase(T)(T value) 784 if (is(T == enum)) 785 { 786 static if (hasUDA!(T, serdeIgnoreCase)) 787 { 788 return true; 789 } 790 else 791 { 792 foreach (i, member; EnumMembers!T) 793 { 794 alias all = __traits(getAttributes, EnumMembers!T[i]); 795 if (value == member) 796 return hasUDA!(EnumMembers!T[i], serdeIgnoreCase); 797 } 798 assert(0); 799 } 800 } 801 802 /// 803 version(mir_test) unittest 804 { 805 enum E 806 { 807 @serdeIgnoreCase 808 a, 809 b, 810 @serdeIgnoreCase 811 c, 812 d, 813 } 814 815 static assert(hasSerdeIgnoreCase(E.a)); 816 static assert(!hasSerdeIgnoreCase(E.b)); 817 static assert(hasSerdeIgnoreCase(E.c)); 818 static assert(!hasSerdeIgnoreCase(E.d)); 819 } 820 821 /// 822 version(mir_test) unittest 823 { 824 @serdeIgnoreCase 825 enum E 826 { 827 a, 828 b, 829 c, 830 d, 831 } 832 833 static assert(hasSerdeIgnoreCase(E.a)); 834 static assert(hasSerdeIgnoreCase(E.b)); 835 static assert(hasSerdeIgnoreCase(E.c)); 836 static assert(hasSerdeIgnoreCase(E.d)); 837 } 838 839 /++ 840 Can be applied only to strings fields. 841 Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation. 842 Use this attributes only for strings or arrays that would not be used after deallocation. 843 +/ 844 enum serdeScoped; 845 846 /++ 847 Attribute that force deserializer to throw an exception that the field hasn't been not found in the input. 848 +/ 849 enum serdeRequired; 850 851 /++ 852 Attribute that allow deserializer to do not throw an exception if the field hasn't been not found in the input. 853 +/ 854 enum serdeOptional; 855 856 /++ 857 Attribute that allow deserializer to don't throw an exception that the field matches multiple keys in the object. 858 +/ 859 enum serdeAllowMultiple; 860 861 /++ 862 Attributes for in transformation. 863 Return type of in transformation must be implicitly convertable to the type of the field. 864 In transformation would be applied after serialization proxy if any. 865 866 +/ 867 struct serdeTransformIn(alias fun) {} 868 869 /++ 870 Returns: unary function of underlaying alias of $(LREF serdeTransformIn) 871 +/ 872 alias serdeGetTransformIn(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]); 873 /// ditto 874 alias serdeGetTransformIn(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeTransformIn))[0]); 875 876 /++ 877 Attributes for out transformation. 878 Return type of out transformation may be differ from the type of the field. 879 Out transformation would be applied before serialization proxy if any. 880 +/ 881 struct serdeTransformOut(alias fun) {} 882 883 /++ 884 Returns: unary function of underlaying alias of $(LREF serdeTransformOut) 885 +/ 886 alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]); 887 /// ditto 888 alias serdeGetTransformOut(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeTransformOut))[0]); 889 890 /++ 891 +/ 892 bool serdeParseEnum(E)(scope const char[] str, scope ref E res) 893 @safe pure nothrow @nogc 894 if (is(E == enum)) 895 { 896 import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex; 897 import mir.utility: _expect; 898 899 uint index = void; 900 if (getEnumIndexFromKey!(E, hasUDA!(E, serdeIgnoreCase), serdeGetKeysIn)(str, index)._expect(true)) 901 { 902 res = unsafeEnumFromIndex!E(index); 903 return true; 904 } 905 return false; 906 } 907 908 version(D_Exceptions) 909 /// ditto 910 auto serdeParseEnum(E)(scope const char[] str) 911 @safe pure 912 if (is(E == enum)) 913 { 914 import mir.utility: max; 915 E ret; 916 if (.serdeParseEnum(str, ret)) 917 return ret; 918 import mir.exception: MirException; 919 throw new MirException("Can't deserialzie ", E.stringof, " from string", str[0 .. max($, 128u)]); 920 } 921 922 /// 923 version(mir_test) unittest 924 { 925 enum E 926 { 927 @serdeKeys("A", "alpha") 928 a, 929 @serdeKeys("B", "beta") 930 b, 931 c, 932 } 933 934 auto e = E.c; 935 assert(serdeParseEnum("A", e)); 936 assert(e == E.a); 937 assert(serdeParseEnum("alpha", e)); 938 assert(e == E.a); 939 assert(serdeParseEnum("beta", e)); 940 assert(e == E.b); 941 assert("B".serdeParseEnum!E == E.b); 942 assert("c".serdeParseEnum!E == E.c); 943 944 assert(!serdeParseEnum("C", e)); 945 assert(!serdeParseEnum("Alpha", e)); 946 } 947 948 /// Case insensitive 949 version(mir_test) unittest 950 { 951 @serdeIgnoreCase // supported for the whole type 952 enum E 953 { 954 @serdeKeys("A", "alpha") 955 a, 956 @serdeKeys("B", "beta") 957 b, 958 c, 959 } 960 961 auto e = E.c; 962 assert(serdeParseEnum("a", e)); 963 assert(e == E.a); 964 assert(serdeParseEnum("alpha", e)); 965 assert(e == E.a); 966 assert(serdeParseEnum("BETA", e)); 967 assert(e == E.b); 968 assert(serdeParseEnum("b", e)); 969 assert(e == E.b); 970 assert(serdeParseEnum("C", e)); 971 assert(e == E.c); 972 } 973 974 /++ 975 Deserialization member type 976 +/ 977 template serdeDeserializationMemberType(T, string member) 978 { 979 import std.traits: Unqual, Parameters; 980 private __gshared T* aggregate; 981 static if (hasField!(T, member)) 982 { 983 alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)); 984 } 985 else 986 static if (__traits(compiles, &__traits(getMember, *aggregate, member)()) || __traits(getOverloads, *aggregate, member).length > 1) 987 { 988 alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)()); 989 } 990 else 991 { 992 alias serdeDeserializationMemberType = Unqual!(Parameters!(__traits(getMember, *aggregate, member))[0]); 993 } 994 } 995 996 /// ditto 997 template serdeDeserializationMemberType(T) 998 { 999 /// 1000 alias serdeDeserializationMemberType(string member) = .serdeDeserializationMemberType!(T, member); 1001 } 1002 1003 1004 /++ 1005 Is deserializable member 1006 +/ 1007 template serdeIsDeserializable(T) 1008 { 1009 /// 1010 enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(T, member).length > 0; 1011 } 1012 1013 /// 1014 version(mir_test) unittest 1015 { 1016 1017 static struct S 1018 { 1019 @serdeIgnore 1020 int i; 1021 1022 @serdeKeys("a", "b") 1023 int a; 1024 } 1025 1026 alias serdeIsDeserializableInS = serdeIsDeserializable!S; 1027 static assert (!serdeIsDeserializableInS!"i"); 1028 static assert (serdeIsDeserializableInS!"a"); 1029 } 1030 1031 /++ 1032 Serialization member type 1033 +/ 1034 template serdeSerializationMemberType(T, string member) 1035 { 1036 import std.traits: Unqual, Parameters; 1037 private __gshared T* aggregate; 1038 static if (hasField!(T, member)) 1039 { 1040 alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)); 1041 } 1042 else 1043 { 1044 alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)()); 1045 } 1046 } 1047 1048 /// ditto 1049 template serdeSerializationMemberType(T) 1050 { 1051 /// 1052 alias serdeSerializationMemberType(string member) = .serdeSerializationMemberType!(T, member); 1053 } 1054 1055 1056 /++ 1057 Is deserializable member 1058 +/ 1059 template serdeIsSerializable(T) 1060 { 1061 /// 1062 enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(T, member) !is null; 1063 } 1064 1065 /// 1066 version(mir_test) unittest 1067 { 1068 1069 static struct S 1070 { 1071 @serdeIgnore 1072 int i; 1073 1074 @serdeKeys("a", "b") 1075 int a; 1076 } 1077 1078 alias serdeIsSerializableInS = serdeIsSerializable!S; 1079 static assert (!serdeIsSerializableInS!"i"); 1080 static assert (serdeIsSerializableInS!"a"); 1081 } 1082 1083 /++ 1084 Final proxy type 1085 +/ 1086 template serdeGetFinalProxy(T) 1087 { 1088 import mir.timestamp: Timestamp; 1089 import std.traits: isAggregateType; 1090 static if (isAggregateType!T || is(T == enum)) 1091 { 1092 static if (hasUDA!(T, serdeProxy)) 1093 { 1094 alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T); 1095 } 1096 else 1097 static if (isAggregateType!T && is(typeof(Timestamp(T.init))) && __traits(getAliasThis, T).length == 0) 1098 { 1099 alias serdeGetFinalProxy = string; 1100 } 1101 else 1102 { 1103 alias serdeGetFinalProxy = T; 1104 } 1105 } 1106 else 1107 { 1108 alias serdeGetFinalProxy = T; 1109 } 1110 } 1111 1112 /// 1113 version(mir_test) unittest 1114 { 1115 1116 @serdeProxy!string 1117 static struct A {} 1118 1119 @serdeProxy!A 1120 static struct B {} 1121 1122 @serdeProxy!B 1123 static struct C {} 1124 1125 static assert (is(serdeGetFinalProxy!C == string), serdeGetFinalProxy!C.stringof); 1126 static assert (is(serdeGetFinalProxy!string == string)); 1127 } 1128 1129 /++ 1130 Final deep proxy type 1131 +/ 1132 template serdeGetFinalDeepProxy(T) 1133 { 1134 import mir.timestamp: Timestamp; 1135 import std.traits: Unqual, isAggregateType, isArray, ForeachType; 1136 static if (isAggregateType!T || is(T == enum)) 1137 { 1138 static if (hasUDA!(T, serdeProxy)) 1139 { 1140 alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T); 1141 } 1142 else 1143 static if (isAggregateType!T && is(typeof(Timestamp(T.init)))) 1144 { 1145 alias serdeGetFinalDeepProxy = string; 1146 } 1147 else 1148 static if (__traits(hasMember, T, "serdeKeysProxy")) 1149 { 1150 alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy); 1151 } 1152 else 1153 // static if (is(T == enum)) 1154 // { 1155 // alias serdeGetFinalDeepProxy = typeof(null); 1156 // } 1157 // else 1158 { 1159 alias serdeGetFinalDeepProxy = T; 1160 } 1161 } 1162 else 1163 static if (isArray!T) 1164 { 1165 alias E = Unqual!(ForeachType!T); 1166 static if (isAggregateType!E || is(E == enum)) 1167 alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E; 1168 else 1169 alias serdeGetFinalDeepProxy = T; 1170 } 1171 else 1172 static if (is(immutable T == immutable V[K], K, V)) 1173 { 1174 alias E = serdeGetFinalDeepProxy!(Unqual!V); 1175 static if (isAggregateType!E || is(E == enum)) 1176 alias serdeGetFinalDeepProxy = E; 1177 else 1178 alias serdeGetFinalDeepProxy = T; 1179 } 1180 else 1181 { 1182 alias serdeGetFinalDeepProxy = T; 1183 } 1184 } 1185 1186 /// 1187 version(mir_test) unittest 1188 { 1189 1190 @serdeProxy!string 1191 static struct A {} 1192 1193 enum E {a,b,c} 1194 1195 @serdeProxy!(A[E]) 1196 static struct B {} 1197 1198 @serdeProxy!(B[]) 1199 static struct C {} 1200 1201 static assert (is(serdeGetFinalDeepProxy!C == A[E])); 1202 static assert (is(serdeGetFinalDeepProxy!string == string)); 1203 } 1204 1205 /++ 1206 Final proxy type deserializable members 1207 +/ 1208 template serdeFinalProxyDeserializableMembers(T) 1209 { 1210 import std.meta: Filter, aliasSeqOf; 1211 alias P = serdeGetFinalProxy!T; 1212 static if (isAggregateType!P || is(P == enum)) 1213 enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))]; 1214 else 1215 // static if (is(P == enum)) 1216 // enum string[] serdeFinalProxyDeserializableMembers = serdeGetKeysIn!P; 1217 // else 1218 enum string[] serdeFinalProxyDeserializableMembers = null; 1219 } 1220 1221 /// 1222 version(mir_test) unittest 1223 { 1224 1225 static struct A 1226 { 1227 @serdeIgnore 1228 int i; 1229 1230 @serdeKeys("a", "b") 1231 int m; 1232 } 1233 1234 @serdeProxy!A 1235 static struct B {} 1236 1237 @serdeProxy!B 1238 static struct C {} 1239 1240 static assert (serdeFinalProxyDeserializableMembers!C == ["m"]); 1241 } 1242 1243 /++ 1244 Final deep proxy type serializable members 1245 +/ 1246 template serdeFinalDeepProxySerializableMembers(T) 1247 { 1248 import std.traits: isAggregateType; 1249 import std.meta: Filter, aliasSeqOf; 1250 alias P = serdeGetFinalDeepProxy!T; 1251 static if (isAggregateType!P || is(P == enum)) 1252 enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))]; 1253 else 1254 // static if (is(P == enum)) 1255 // enum string[] serdeFinalDeepProxySerializableMembers = [serdeGetKeyOut!P]; 1256 // else 1257 enum string[] serdeFinalDeepProxySerializableMembers = null; 1258 } 1259 1260 /// 1261 version(mir_test) unittest 1262 { 1263 1264 static struct A 1265 { 1266 @serdeIgnore 1267 int i; 1268 1269 @serdeKeys("a", "b") 1270 int m; 1271 } 1272 1273 @serdeProxy!(A[string]) 1274 static struct B {} 1275 1276 @serdeProxy!(B[]) 1277 static struct C {} 1278 1279 static assert (serdeFinalDeepProxySerializableMembers!C == ["m"]); 1280 } 1281 1282 /++ 1283 Final proxy type deserializable members 1284 +/ 1285 template serdeFinalProxySerializableMembers(T) 1286 { 1287 import std.meta: Filter, aliasSeqOf; 1288 alias P = serdeGetFinalProxy!T; 1289 static if (isAggregateType!P || is(P == enum)) 1290 enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))]; 1291 else 1292 // static if (is(P == enum)) 1293 // enum string[] serdeFinalProxySerializableMembers = [serdeGetKeyOut!P]; 1294 // else 1295 enum string[] serdeFinalProxySerializableMembers = null; 1296 } 1297 1298 /// 1299 version(mir_test) unittest 1300 { 1301 1302 static struct A 1303 { 1304 @serdeIgnore 1305 int i; 1306 1307 @serdeKeys("a", "b") 1308 int m; 1309 } 1310 1311 @serdeProxy!A 1312 static struct B {} 1313 1314 @serdeProxy!B 1315 static struct C {} 1316 1317 static assert (serdeFinalProxySerializableMembers!C == ["m"]); 1318 } 1319 1320 /++ 1321 Final deep proxy type serializable members 1322 +/ 1323 template serdeFinalDeepProxyDeserializableMembers(T) 1324 { 1325 import std.traits: isAggregateType; 1326 import std.meta: Filter, aliasSeqOf; 1327 alias P = serdeGetFinalDeepProxy!T; 1328 static if (isAggregateType!P || is(P == enum)) 1329 enum string[] serdeFinalDeepProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))]; 1330 else 1331 // static if (is(P == enum)) 1332 // enum string[] serdeFinalDeepProxyDeserializableMembers = serdeGetKeysIn!P; 1333 // else 1334 enum string[] serdeFinalDeepProxyDeserializableMembers = null; 1335 } 1336 1337 /// 1338 version(mir_test) unittest 1339 { 1340 static struct A 1341 { 1342 @serdeIgnore 1343 int i; 1344 1345 @serdeKeys("a", "b") 1346 int m; 1347 } 1348 1349 @serdeProxy!(A[string]) 1350 static struct B {} 1351 1352 @serdeProxy!(B[]) 1353 static struct C {} 1354 1355 static assert (serdeFinalDeepProxyDeserializableMembers!C == ["m"]); 1356 } 1357 1358 /++ 1359 Deserialization member final proxy type 1360 +/ 1361 template serdeFinalDeserializationMemberType(T, string member) 1362 { 1363 static if (hasUDA!(T, member, serdeProxy)) 1364 { 1365 alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(T, member)); 1366 } 1367 else 1368 { 1369 alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeDeserializationMemberType!(T, member)); 1370 } 1371 } 1372 1373 /// ditto 1374 template serdeFinalDeserializationMemberType(T) 1375 { 1376 /// 1377 alias serdeFinalDeserializationMemberType(string member) = .serdeFinalDeserializationMemberType!(T, member); 1378 } 1379 1380 /// 1381 version(mir_test) unittest 1382 { 1383 1384 static struct A 1385 { 1386 1387 } 1388 1389 @serdeProxy!A 1390 static struct B {} 1391 1392 @serdeProxy!B 1393 static struct C {} 1394 1395 1396 @serdeProxy!double 1397 struct E {} 1398 1399 struct D 1400 { 1401 C c; 1402 1403 @serdeProxy!E 1404 int d; 1405 } 1406 1407 static assert (is(serdeFinalDeserializationMemberType!(D, "c") == A)); 1408 static assert (is(serdeFinalDeserializationMemberType!(D, "d") == double)); 1409 } 1410 1411 /++ 1412 Deserialization members final proxy types 1413 +/ 1414 template serdeDeserializationFinalProxyMemberTypes(T) 1415 { 1416 import std.meta: NoDuplicates, staticMap, aliasSeqOf; 1417 alias serdeDeserializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalProxyDeserializableMembers!T)))); 1418 } 1419 1420 /// 1421 version(mir_test) unittest 1422 { 1423 1424 static struct A {} 1425 1426 @serdeProxy!A 1427 static struct B {} 1428 1429 @serdeProxy!B 1430 static struct C {} 1431 1432 @serdeProxy!B 1433 static struct E {} 1434 1435 static struct D 1436 { 1437 C c; 1438 1439 @serdeProxy!E 1440 int d; 1441 } 1442 1443 import std.meta: AliasSeq; 1444 static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A)); 1445 } 1446 1447 /++ 1448 Serialization member final proxy type 1449 +/ 1450 template serdeFinalSerializationMemberType(T, string member) 1451 { 1452 static if (hasUDA!(T, member, serdeProxy)) 1453 { 1454 alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(T, member)); 1455 } 1456 else 1457 { 1458 alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeSerializationMemberType!(T, member)); 1459 } 1460 } 1461 1462 /// ditto 1463 template serdeFinalSerializationMemberType(T) 1464 { 1465 /// 1466 alias serdeFinalSerializationMemberType(string member) = .serdeFinalSerializationMemberType!(T, member); 1467 } 1468 1469 /// 1470 version(mir_test) unittest 1471 { 1472 1473 static struct A 1474 { 1475 1476 } 1477 1478 @serdeProxy!A 1479 static struct B {} 1480 1481 @serdeProxy!B 1482 static struct C {} 1483 1484 1485 @serdeProxy!double 1486 struct E {} 1487 1488 struct D 1489 { 1490 C c; 1491 1492 @serdeProxy!E 1493 int d; 1494 } 1495 1496 static assert (is(serdeFinalSerializationMemberType!(D, "c") == A), serdeFinalSerializationMemberType!(D, "c")); 1497 static assert (is(serdeFinalSerializationMemberType!(D, "d") == double)); 1498 } 1499 1500 /++ 1501 Serialization members final proxy types 1502 +/ 1503 template serdeSerializationFinalProxyMemberTypes(T) 1504 { 1505 import std.meta: NoDuplicates, staticMap, aliasSeqOf; 1506 alias serdeSerializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalProxySerializableMembers!T)))); 1507 } 1508 1509 /// 1510 version(mir_test) unittest 1511 { 1512 1513 static struct A {} 1514 1515 @serdeProxy!A 1516 static struct B {} 1517 1518 @serdeProxy!B 1519 static struct C {} 1520 1521 @serdeProxy!B 1522 static struct E {} 1523 1524 static struct D 1525 { 1526 C c; 1527 1528 @serdeProxy!E 1529 int d; 1530 } 1531 1532 import std.meta: AliasSeq; 1533 static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A)); 1534 } 1535 1536 /++ 1537 Deserialization members final deep proxy types 1538 +/ 1539 template serdeDeserializationFinalDeepProxyMemberTypes(T) 1540 { 1541 import std.meta: NoDuplicates, staticMap, aliasSeqOf; 1542 import mir.algebraic: isVariant; 1543 static if (isVariant!T) 1544 alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes)); 1545 else 1546 static if (isAlgebraicAliasThis!T) 1547 { 1548 private __gshared T* aggregate; 1549 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 1550 alias serdeDeserializationFinalDeepProxyMemberTypes = .serdeDeserializationFinalDeepProxyMemberTypes!A; 1551 } 1552 else 1553 alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))); 1554 } 1555 1556 /// 1557 version(mir_test) unittest 1558 { 1559 1560 static struct A {} 1561 1562 @serdeProxy!(A[]) 1563 static struct B {} 1564 1565 enum R {a, b, c} 1566 1567 @serdeProxy!(B[R]) 1568 static struct C {} 1569 1570 @serdeProxy!(B[string]) 1571 static struct E {} 1572 1573 static struct D 1574 { 1575 C c; 1576 1577 @serdeProxy!E 1578 int d; 1579 } 1580 1581 import std.meta: AliasSeq; 1582 static assert (is(serdeDeserializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeDeserializationFinalDeepProxyMemberTypes!D); 1583 } 1584 1585 /++ 1586 Serialization members final deep proxy types 1587 +/ 1588 template serdeSerializationFinalDeepProxyMemberTypes(T) 1589 { 1590 import std.meta: NoDuplicates, staticMap, aliasSeqOf; 1591 import mir.algebraic: isVariant; 1592 static if (isVariant!T) 1593 alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes)); 1594 else 1595 static if (isAlgebraicAliasThis!T) 1596 { 1597 private __gshared T* aggregate; 1598 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 1599 alias serdeSerializationFinalDeepProxyMemberTypes = .serdeSerializationFinalDeepProxyMemberTypes!A; 1600 } 1601 else 1602 alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))); 1603 } 1604 1605 /// 1606 version(mir_test) unittest 1607 { 1608 1609 static struct A {} 1610 1611 @serdeProxy!(A[]) 1612 static struct B {} 1613 1614 enum R {a, b, c} 1615 1616 @serdeProxy!(B[R]) 1617 static struct C {} 1618 1619 @serdeProxy!(B[string]) 1620 static struct E {} 1621 1622 static struct D 1623 { 1624 C c; 1625 1626 @serdeProxy!E 1627 int d; 1628 } 1629 1630 import std.meta: AliasSeq; 1631 static assert (is(serdeSerializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalDeepProxyMemberTypes!D); 1632 } 1633 1634 private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...) 1635 { 1636 import std.meta: NoDuplicates, staticMap; 1637 alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalProxyMemberTypes, T)); 1638 static if (F.length == T.length) 1639 alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = T; 1640 else 1641 alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = .serdeDeserializationFinalProxyMemberTypesRecurseImpl!F; 1642 } 1643 1644 /++ 1645 Deserialization members final proxy types (recursive) 1646 +/ 1647 alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserializationFinalProxyMemberTypesRecurseImpl!(serdeGetFinalProxy!T); 1648 1649 /// 1650 version(mir_test) unittest 1651 { 1652 1653 static struct A { double g; } 1654 1655 @serdeProxy!A 1656 static struct B {} 1657 1658 @serdeProxy!B 1659 static struct C {} 1660 1661 @serdeProxy!B 1662 static struct E {} 1663 1664 static struct D 1665 { 1666 C c; 1667 1668 @serdeProxy!E 1669 int d; 1670 } 1671 1672 @serdeProxy!D 1673 static struct F {} 1674 1675 import std.meta: AliasSeq; 1676 static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double))); 1677 } 1678 1679 private template serdeSerializationFinalDeepProxyMemberTypesRecurseImpl(T...) 1680 { 1681 import std.meta: NoDuplicates, staticMap; 1682 alias F = NoDuplicates!(T, staticMap!(serdeSerializationFinalDeepProxyMemberTypes, T)); 1683 static if (F.length == T.length) 1684 alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = T; 1685 else 1686 alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = .serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!F; 1687 } 1688 1689 private template serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl(T...) 1690 { 1691 import std.meta: NoDuplicates, staticMap; 1692 alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalDeepProxyMemberTypes, T)); 1693 static if (F.length == T.length) 1694 alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = T; 1695 else 1696 alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = .serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!F; 1697 } 1698 1699 /++ 1700 Deserialization members final deep proxy types (recursive) 1701 +/ 1702 alias serdeDeserializationFinalDeepProxyMemberTypesRecurse(T) = serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T); 1703 1704 /// 1705 version(mir_test) unittest 1706 { 1707 1708 static struct A { double g; } 1709 1710 @serdeProxy!(A[]) 1711 static struct B {} 1712 1713 @serdeProxy!(B[string]) 1714 static struct C {} 1715 1716 @serdeProxy!B 1717 static struct E {} 1718 1719 static struct D 1720 { 1721 C c; 1722 1723 @serdeProxy!(E[]) 1724 int d; 1725 } 1726 1727 @serdeProxy!D 1728 static struct F {} 1729 1730 import std.meta: AliasSeq; 1731 static assert (is(serdeDeserializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double))); 1732 } 1733 1734 /++ 1735 Serialization members final deep proxy types (recursive) 1736 +/ 1737 alias serdeSerializationFinalDeepProxyMemberTypesRecurse(T) = serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T); 1738 1739 /// 1740 version(mir_test) unittest 1741 { 1742 1743 static struct A { double g; } 1744 1745 @serdeProxy!(A[]) 1746 static struct B {} 1747 1748 @serdeProxy!(B[string]) 1749 static struct C {} 1750 1751 @serdeProxy!B 1752 static struct E {} 1753 1754 static struct D 1755 { 1756 C c; 1757 1758 @serdeProxy!(E[]) 1759 int d; 1760 } 1761 1762 @serdeProxy!D 1763 static struct F {} 1764 1765 import std.meta: AliasSeq; 1766 static assert (is(serdeSerializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)), serdeSerializationFinalDeepProxyMemberTypesRecurse!F); 1767 } 1768 1769 package string[] sortUniqKeys()(string[] keys) 1770 @safe pure nothrow 1771 { 1772 import mir.algorithm.iteration: uniq; 1773 import mir.array.allocation: array; 1774 import mir.ndslice.sorting: sort; 1775 1776 return keys 1777 .sort!((a, b) { 1778 if (sizediff_t d = a.length - b.length) 1779 return d < 0; 1780 return a < b; 1781 }) 1782 .uniq 1783 .array; 1784 } 1785 1786 1787 private template serdeGetKeysIn2(T) 1788 { 1789 // T* value; 1790 enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(T, member); 1791 } 1792 1793 private template serdeGetKeyOut2(T) 1794 { 1795 enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(T, member) is null ? null : [serdeGetKeyOut!(T, member)]; 1796 } 1797 1798 private template serdeFinalDeepProxyDeserializableMemberKeys(T) 1799 { 1800 import std.meta: staticMap, aliasSeqOf; 1801 import std.traits: isAggregateType; 1802 1803 static if (isAggregateType!T) 1804 { 1805 import mir.algebraic: isVariant; 1806 static if (isVariant!T) 1807 enum string[] serdeFinalDeepProxyDeserializableMemberKeys = getAlgebraicAnnotationsOfVariant!T; 1808 else 1809 enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))]; 1810 } 1811 else 1812 static if (is(T == enum)) 1813 { 1814 enum string[] serdeFinalDeepProxyDeserializableMemberKeys = enumAllKeysIn!T; 1815 } 1816 else 1817 enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null; 1818 } 1819 1820 package template getAlgebraicAnnotationsOfVariant(T) 1821 { 1822 import std.meta: staticMap, Filter; 1823 enum string[] getAlgebraicAnnotationsOfVariant = [staticMap!(serdeGetAlgebraicAnnotation, Filter!(serdeHasAlgebraicAnnotation, T.AllowedTypes))]; 1824 } 1825 1826 private template serdeFinalDeepProxySerializableMemberKeys(T) 1827 { 1828 import std.meta: staticMap, aliasSeqOf; 1829 import std.traits: isAggregateType; 1830 1831 static if (isAggregateType!T) 1832 { 1833 import mir.algebraic: isVariant; 1834 static if (isVariant!T) 1835 enum string[] serdeFinalDeepProxySerializableMemberKeys = getAlgebraicAnnotationsOfVariant!T; 1836 else 1837 enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))]; 1838 } 1839 else 1840 static if (is(T == enum)) 1841 { 1842 enum string[] serdeFinalDeepProxySerializableMemberKeys = enumAllKeysOut!T; 1843 } 1844 else 1845 enum string[] serdeFinalDeepProxySerializableMemberKeys = null; 1846 } 1847 1848 private template serdeGetAlgebraicAnnotations(T) 1849 { 1850 static if (isAggregateType!T || is(T == enum)) 1851 static if (hasUDA!(T, serdeAlgebraicAnnotation)) 1852 enum string[] serdeGetAlgebraicAnnotations = [getUDA!(T, serdeAlgebraicAnnotation).annotation]; 1853 else 1854 enum string[] serdeGetAlgebraicAnnotations = null; 1855 else 1856 enum string[] serdeGetAlgebraicAnnotations = null; 1857 } 1858 1859 package template serdeIsComplexVariant(T) 1860 { 1861 import mir.algebraic: isVariant, isNullable; 1862 static if (isVariant!T) 1863 { 1864 enum serdeIsComplexVariant = (T.AllowedTypes.length - isNullable!T) > 1; 1865 } 1866 else 1867 { 1868 enum bool serdeIsComplexVariant = false; 1869 } 1870 } 1871 1872 package template isAlgebraicAliasThis(T) 1873 { 1874 static if (__traits(getAliasThis, T).length) 1875 { 1876 import mir.algebraic: isVariant; 1877 private __gshared T* aggregate; 1878 alias A = AliasSeq!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)))); 1879 static if (A.length != 1) 1880 enum isAlgebraicAliasThis = false; 1881 else 1882 static if (isVariant!(A[0])) 1883 enum isAlgebraicAliasThis = true; 1884 else 1885 enum isAlgebraicAliasThis = serdeIsDynamicAlgebraic!(A[0]); 1886 } 1887 else 1888 { 1889 enum isAlgebraicAliasThis = false; 1890 } 1891 } 1892 1893 /++ 1894 Serialization members final proxy keys (recursive) 1895 +/ 1896 template serdeGetSerializationKeysRecurse(T) 1897 { 1898 import std.meta: staticMap, aliasSeqOf; 1899 enum string[] serdeGetSerializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxySerializableMemberKeys, serdeSerializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys; 1900 } 1901 1902 /// 1903 version(mir_test) unittest 1904 { 1905 enum Y 1906 { 1907 a, 1908 b, 1909 c, 1910 } 1911 1912 static struct A { double g; float d; } 1913 1914 @serdeProxy!A 1915 static struct B { int f; } 1916 1917 @serdeProxy!(B[Y][string]) 1918 static union C { int f; } 1919 1920 @serdeProxy!(B[]) 1921 static interface E { int f() @property; } 1922 1923 enum N { a, b } 1924 1925 static class D 1926 { 1927 C c; 1928 1929 @serdeProxy!(E[]) 1930 int d; 1931 1932 N e; 1933 } 1934 1935 @serdeAlgebraicAnnotation("$F") 1936 @serdeProxy!D 1937 static struct F { int f; } 1938 1939 static assert (serdeGetSerializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]); 1940 1941 import mir.algebraic; 1942 static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]); 1943 } 1944 1945 /++ 1946 Deserialization members final proxy keys (recursive) 1947 +/ 1948 template serdeGetDeserializationKeysRecurse(T) 1949 { 1950 import std.meta: staticMap, aliasSeqOf; 1951 enum string[] serdeGetDeserializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys; 1952 } 1953 1954 /// 1955 version(mir_test) unittest 1956 { 1957 1958 static struct A { double g; float d; } 1959 1960 @serdeProxy!A 1961 static struct B { int f; } 1962 1963 @serdeProxy!(B[string]) 1964 static union C { int f; } 1965 1966 @serdeProxy!(B[]) 1967 static interface E { int f() @property; } 1968 1969 enum N { a, b } 1970 1971 static class D 1972 { 1973 C c; 1974 1975 @serdeProxy!(E[]) 1976 int d; 1977 1978 N e; 1979 } 1980 1981 @serdeAlgebraicAnnotation("$F") 1982 @serdeProxy!D 1983 static struct F { int f; } 1984 1985 static assert (serdeGetDeserializationKeysRecurse!N == ["a", "b"], serdeGetDeserializationKeysRecurse!N); 1986 1987 static assert (serdeGetDeserializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]); 1988 1989 import mir.algebraic; 1990 static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]); 1991 } 1992 1993 /++ 1994 UDA used to force deserializer to initilize members in the order of their definition in the target object/structure. 1995 1996 The attribute force deserializer to create a dummy type (recursively), initializer its fields and then assign them to 1997 to the object members (fields and setters) in the order of their definition. 1998 1999 See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn), $(LREF serdeIgnoreInIfAggregate). 2000 +/ 2001 enum serdeOrderedIn; 2002 2003 /++ 2004 UDA used to force deserializer to initilize members in the order of their definition in the target object/structure. 2005 2006 Unlike $(LREF serdeOrderedIn) `serdeRealOrderedDummy` force deserialzier to iterate all DOM keys for each object deserialization member. 2007 It is slower but more universal approach. 2008 2009 See_also: $(LREF serdeOrderedIn), $(LREF serdeIgnoreInIfAggregate) 2010 +/ 2011 enum serdeRealOrderedIn; 2012 2013 2014 /++ 2015 UDA used to force deserializer to skip the member final deserialization. 2016 A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method 2017 and dummy method `serdeFinalizeTargetMember`. 2018 +/ 2019 enum serdeFromDummyByUser; 2020 2021 /++ 2022 UDA used to force serializer to output members in the alphabetical order of their output keys. 2023 +/ 2024 enum serdeAlphabetOut; 2025 2026 /++ 2027 A dummy structure usefull $(LREF serdeOrderedIn) support. 2028 +/ 2029 struct SerdeOrderedDummy(T, bool __optionalByDefault = false) 2030 if (is(serdeGetFinalProxy!T == T) && isAggregateType!T) 2031 { 2032 2033 @serdeIgnore 2034 SerdeFlags!(typeof(this)) __serdeFlags; 2035 2036 static if (__optionalByDefault) 2037 alias __serdeOptionalRequired = serdeRequired; 2038 else 2039 alias __serdeOptionalRequired = serdeOptional; 2040 2041 this()(T value) 2042 { 2043 static foreach (member; serdeFinalProxyDeserializableMembers!T) 2044 { 2045 static if (hasField!(T, member)) 2046 { 2047 static if (__traits(compiles, {__traits(getMember, this, member) = __traits(getMember, value, member);})) 2048 __traits(getMember, this, member) = __traits(getMember, value, member); 2049 } 2050 } 2051 } 2052 2053 public: 2054 2055 static foreach (i, member; serdeFinalProxyDeserializableMembers!T) 2056 { 2057 static if (hasField!(T, member)) 2058 { 2059 static if (hasUDA!(T, member, serdeProxy)) 2060 { 2061 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";"); 2062 } 2063 else 2064 static if (isAggregateType!(typeof(__traits(getMember, T, member)))) 2065 { 2066 static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy)) 2067 { 2068 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";"); 2069 } 2070 else 2071 static if (__traits(compiles, { 2072 mixin("enum SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");"); 2073 })) 2074 { 2075 mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");"); 2076 } 2077 else 2078 { 2079 mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ ";"); 2080 } 2081 } 2082 else 2083 { 2084 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";"); 2085 } 2086 } 2087 else 2088 { 2089 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";"); 2090 } 2091 } 2092 2093 /// Initialize target members 2094 void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags) 2095 { 2096 __serdeFlags = flags; 2097 } 2098 2099 /// Initialize target members 2100 void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags) 2101 { 2102 import std.traits: hasElaborateAssign; 2103 static foreach (member; serdeFinalProxyDeserializableMembers!T) 2104 __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member); 2105 static foreach (member; serdeFinalProxyDeserializableMembers!T) 2106 static if (!hasUDA!(T, member, serdeFromDummyByUser)) 2107 {{ 2108 if (hasUDA!(T, member, __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member)) 2109 { 2110 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I)) 2111 { 2112 alias M = typeof(__traits(getMember, value, member)); 2113 SerdeFlags!M memberFlags; 2114 __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags); 2115 static if (__traits(hasMember, M, "serdeFinalizeWithFlags")) 2116 { 2117 __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags); 2118 } 2119 static if (__traits(hasMember, M, "serdeFinalize")) 2120 { 2121 __traits(getMember, value, member).serdeFinalize(); 2122 } 2123 } 2124 else 2125 { 2126 static if (hasElaborateAssign!(typeof(__traits(getMember, this, member)))) 2127 { 2128 import core.lifetime: move; 2129 __traits(getMember, value, member) = move(__traits(getMember, this, member)); 2130 } 2131 else 2132 __traits(getMember, value, member) = __traits(getMember, this, member); 2133 } 2134 } 2135 }} 2136 static if (__traits(hasMember, T, "serdeFinalizeWithDummy")) 2137 { 2138 value.serdeFinalizeWithDummy(this); 2139 } 2140 } 2141 2142 /// Initialize target member 2143 void serdeFinalizeTargetMember(string member)(ref T value) 2144 { 2145 if (hasUDA!(T, member, __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member)) 2146 { 2147 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I)) 2148 { 2149 alias M = typeof(__traits(getMember, value, member)); 2150 SerdeFlags!M memberFlags; 2151 __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags); 2152 static if (__traits(hasMember, M, "serdeFinalizeWithFlags")) 2153 { 2154 __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags); 2155 } 2156 static if (__traits(hasMember, M, "serdeFinalize")) 2157 { 2158 __traits(getMember, value, member).serdeFinalize(); 2159 } 2160 } 2161 else 2162 { 2163 static if (hasElaborateAssign!(typeof(__traits(getMember, this, member)))) 2164 { 2165 import core.lifetime: move; 2166 __traits(getMember, value, member) = move(__traits(getMember, this, member)); 2167 } 2168 else 2169 __traits(getMember, value, member) = __traits(getMember, this, member); 2170 } 2171 } 2172 } 2173 } 2174 2175 /// 2176 version(mir_test) unittest 2177 { 2178 import std.traits; 2179 2180 static struct S 2181 { 2182 private double _d; 2183 2184 @serdeProxy!int 2185 void d(double v) @property { _d = v; } 2186 2187 string s; 2188 } 2189 2190 static assert(is(typeof(SerdeOrderedDummy!S.init.d) == double), SerdeOrderedDummy!S.init.d); 2191 static assert(is(typeof(SerdeOrderedDummy!S.init.s) == string)); 2192 static assert(hasUDA!(S.d, serdeProxy)); 2193 static assert(hasUDA!(SerdeOrderedDummy!S.d, serdeProxy)); 2194 } 2195 2196 /++ 2197 A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method. 2198 +/ 2199 struct SerdeFlags(T) 2200 { 2201 static if (is(T : SerdeOrderedDummy!I, I)) 2202 static foreach(member; serdeFinalProxyDeserializableMembers!I) 2203 mixin("bool " ~ member ~ ";"); 2204 else 2205 static foreach(member; serdeFinalProxyDeserializableMembers!T) 2206 mixin("bool " ~ member ~ ";"); 2207 } 2208 2209 /++ 2210 The UDA used on struct or class definitions to denote an discriminated field and its tag 2211 for algebraic deserialization. 2212 2213 Discriminated field is ignored during the annotated structure/class deseriliazation. 2214 +/ 2215 struct serdeDiscriminatedField 2216 { 2217 /// Name of the field in the JSON like data object. 2218 string field; 2219 /// Value of the field for the current type. 2220 string tag; 2221 } 2222 2223 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped) 2224 { 2225 /// 2226 SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, scope ref T value, scope ref SerdeFlags!T requiredFlags, scope ref Context context) 2227 { 2228 import core.lifetime: move; 2229 import mir.conv: to; 2230 2231 enum likeList = hasUDA!(T, member, serdeLikeList); 2232 enum likeStruct = hasUDA!(T, member, serdeLikeStruct); 2233 enum hasProxy = hasUDA!(T, member, serdeProxy); 2234 enum hasScoped = hasUDA!(T, member, serdeScoped); 2235 2236 static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes"); 2237 static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization"); 2238 static assert (hasProxy >= likeList, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization"); 2239 2240 alias Member = serdeDeserializationMemberType!(T, member); 2241 2242 static if (hasProxy) 2243 alias Temporal = serdeGetProxy!(T, member); 2244 else 2245 alias Temporal = Member; 2246 2247 static if (hasScoped) 2248 static if (__traits(compiles, { Temporal temporal; deserializeScoped(data, temporal); })) 2249 alias impl = deserializeScoped; 2250 else 2251 alias impl = deserializeValue; 2252 else 2253 alias impl = deserializeValue; 2254 2255 static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed."); 2256 2257 static if (!hasUDA!(T, member, serdeAllowMultiple)) 2258 if (__traits(getMember, requiredFlags, member)) 2259 return excm!member; 2260 2261 __traits(getMember, requiredFlags, member) = true; 2262 2263 static if (likeList) 2264 { 2265 foreach(elem; data.byElement) 2266 { 2267 Temporal temporal; 2268 if (auto exc = impl(elem, temporal, context)) 2269 return exc; 2270 __traits(getMember, value, member).put(move(temporal)); 2271 } 2272 } 2273 else 2274 static if (likeStruct) 2275 { 2276 foreach(v; data.byKeyValue(context)) 2277 { 2278 Temporal temporal; 2279 if (auto exc = impl(v.value, temporal, context)) 2280 return exc; 2281 __traits(getMember, value, member)[v.key.idup] = move(temporal); 2282 } 2283 } 2284 else 2285 static if (hasProxy) 2286 { 2287 Temporal temporal; 2288 if (auto exc = impl(data, temporal, context)) 2289 return exc; 2290 __traits(getMember, value, member) = to!(serdeDeserializationMemberType!(T, member))(move(temporal)); 2291 } 2292 else 2293 static if (hasField!(T, member)) 2294 { 2295 if (auto exc = impl(data, __traits(getMember, value, member), context)) 2296 return exc; 2297 } 2298 else 2299 { 2300 Member temporal; 2301 if (auto exc = impl(data, temporal, context)) 2302 return exc; 2303 __traits(getMember, value, member) = move(temporal); 2304 } 2305 2306 static if (hasUDA!(T, member, serdeTransformIn)) 2307 { 2308 alias transform = serdeGetTransformIn!(T, member, member); 2309 static if (hasField!(T, member)) 2310 { 2311 transform(__traits(getMember, value, member)); 2312 } 2313 else 2314 { 2315 auto temporal = __traits(getMember, value, member); 2316 transform(temporal); 2317 __traits(getMember, value, member) = move(temporal); 2318 } 2319 } 2320 2321 return null; 2322 } 2323 } 2324 2325 private: 2326 2327 auto fastLazyToUpper()(return scope const(char)[] name) 2328 { 2329 import mir.ndslice.topology: map; 2330 return name.map!fastToUpper; 2331 } 2332 2333 auto fastToUpper()(char a) 2334 { // std.ascii may not be inlined 2335 return 'a' <= a && a <= 'z' ? cast(char)(a ^ 0x20) : a; 2336 } 2337 2338 @safe pure nothrow @nogc 2339 char[] fastToUpperInPlace()(return scope char[] a) 2340 { 2341 foreach(ref char e; a) 2342 e = e.fastToUpper; 2343 return a; 2344 } 2345 2346 template enumAllKeysIn(T) 2347 if (is(T == enum)) 2348 { 2349 import std.traits: EnumMembers; 2350 import std.meta: staticMap, aliasSeqOf; 2351 enum string[] enumAllKeysIn = [staticMap!(aliasSeqOf, staticMap!(.serdeGetKeysIn, EnumMembers!T))]; 2352 } 2353 2354 template enumAllKeysOut(T) 2355 if (is(T == enum)) 2356 { 2357 import std.traits: EnumMembers; 2358 import std.meta: staticMap, aliasSeqOf; 2359 enum string[] enumAllKeysOut = [staticMap!(.serdeGetKeyOut, EnumMembers!T)]; 2360 }