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