1 /** 2 * The module contains serialization functions 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-01-12 8 */ 9 10 module uninode.serialization; 11 12 public import uninode.node : UniNode; 13 14 private 15 { 16 import std.typetuple : TypeTuple; 17 import std.exception : enforce; 18 import std.conv : text, to; 19 import std.typecons; 20 import std.traits; 21 import std.meta; 22 23 import bolts : FilterMembersOf; 24 25 import uninode.node; 26 } 27 28 ///Marks a method for use in serialization 29 enum SerializationMethod; 30 31 ///Marks a method for use in deserialization 32 enum DeserializationMethod; 33 34 35 /** 36 * Attribute for overriding the field name during (de-)serialization. 37 */ 38 NameAttribute name(string name) @safe nothrow pure @property 39 { 40 return NameAttribute(name); 41 } 42 43 44 /** 45 * Attribute for forcing serialization of enum fields by name instead of by value. 46 */ 47 ByNameAttribyte byName() @safe nothrow pure @property 48 { 49 return ByNameAttribyte(); 50 } 51 52 53 /** 54 * Attribute for representing a struct/class as an array instead of an object. 55 */ 56 AsArrayAttribute asArray() @safe nothrow pure @property 57 { 58 return AsArrayAttribute(); 59 } 60 61 62 /** 63 * Attribute for marking non-serialized fields. 64 */ 65 IgnoreAttribute ignore() pure nothrow @safe @property 66 { 67 return IgnoreAttribute(); 68 } 69 70 71 /** 72 * Attribute for marking non-serialized fields. 73 */ 74 OptionalAttribute optional() pure nothrow @safe @property 75 { 76 return OptionalAttribute(); 77 } 78 79 80 /** 81 * Attribute marking a field as masked during serialization. 82 */ 83 MaskedAttribute masked() pure nothrow @safe @property 84 { 85 return MaskedAttribute(); 86 } 87 88 89 /** 90 * Attribute for forcing serialization as string. 91 */ 92 AsStringAttribute asString() pure nothrow @safe @property 93 { 94 return AsStringAttribute(); 95 } 96 97 98 /** 99 * Default UniNode serializer 100 */ 101 struct UniNodeSerializer {} 102 103 104 /** 105 * Serialize object to UniNode 106 * 107 * Params: 108 * object = serialized object 109 */ 110 UniNode serializeToUniNode(T)(auto ref const T value) 111 { 112 return serialize!(UniNode, UniNodeSerializer)(value); 113 } 114 115 116 /** 117 * Deserialize object form UniNode 118 * 119 * Params: 120 * src = UniNode value 121 */ 122 T deserializeUniNode(T)(UniNode src) 123 { 124 T value; 125 deserialize!(UniNode, UniNodeSerializer)(src, value); 126 return value; 127 } 128 129 130 /** 131 * Serializes a value with Serializer 132 */ 133 template serialize(Node, Serializer : UniNodeSerializer) 134 if (isUniNode!Node) 135 { 136 Node serialize(T)(auto ref const T value) 137 { 138 static if (__traits(compiles, __traits(getAttributes, T))) 139 { 140 alias TA = TypeTuple!(__traits(getAttributes, T)); 141 return serializeValue!(T, TA)(value); 142 } 143 else 144 return serializeValue!(T)(value); 145 } 146 147 148 private: 149 150 151 Node serializeValue(T, A...)(auto ref const T value) 152 { 153 alias TU = Unqual!T; 154 155 static if (is(TU == typeof(null))) 156 return Node(); 157 else static if (is(TU : Node)) 158 return value; 159 else static if (isNullable!T) 160 { 161 if (value.isNull) 162 return serializeValue!(A)(null); 163 else 164 return serializeValue!(TemplateArgsOf!T[0], A)(value.get); 165 } 166 else static if (is(T == enum)) 167 { 168 static if (hasAttribute!(ByNameAttribyte, A)) 169 return serializeValue!(string, A)(value.text); 170 else 171 return serializeValue!(OriginalType!TU, A)(cast(OriginalType!TU)value); 172 } 173 else static if (isInstanceOf!(Typedef, TU)) 174 return serializeValue!(TypedefType!TU, A)(cast(TypedefType!TU)value); 175 else static if (isPointer!T) 176 { 177 if (value is null) 178 return Node(); 179 return serializeValue!(PointerTarget!TU, A)(*value); 180 } 181 else static if (isTimeType!T) 182 return serializeValue!(string, A)(value.toISOExtString); 183 else static if (isSomeChar!T) 184 return serializeValue!(string, A)(value.text); 185 else static if (hasAttribute!(AsStringAttribute, A)) 186 return serializeValue!(string)(value.text); 187 else static if (is(T == Tuple!TPS, TPS...)) 188 { 189 import std.algorithm.searching: all; 190 enum fieldsCount = TU.Types.length; 191 192 static if (all!"!a.empty"([TU.fieldNames]) && !hasAttribute!(AsArrayAttribute, A)) 193 { 194 Node[string] output; 195 foreach (i, _; TU.Types) 196 { 197 alias TV = typeof(value[i]); 198 enum memberName = underscoreStrip(TU.fieldNames[i]); 199 output[memberName] = serializeValue!(TV, A)(value[i]); 200 } 201 return Node(output); 202 } 203 else static if (fieldsCount == 1) 204 return serializeValue!(typeof(value[0]), A)(value[0]); 205 else 206 { 207 Node[] output; 208 output.reserve(fieldsCount); 209 foreach (i, _; TU.Types) 210 { 211 alias TV = typeof(value[i]); 212 output ~= serializeValue!(typeof(value[i]), A)(value[i]); 213 } 214 return Node(output); 215 } 216 } 217 else static if (isAssociativeArray!T) 218 { 219 alias TK = KeyType!TU; 220 alias TV = Unqual!(ValueType!TU); 221 222 Node[string] output; 223 foreach (key, ref el; value) 224 { 225 string keyname; 226 static if (is(TK : string)) 227 keyname = key; 228 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) 229 keyname = key.text; 230 else static assert(false, "Associative array keys must be strings," ~ 231 "numbers, enums."); 232 output[keyname] = serializeValue!(TV)(el); 233 } 234 return Node(output); 235 } 236 else static if (is(TU == BitFlags!E, E)) 237 { 238 size_t cnt = 0; 239 foreach (v; EnumMembers!E) 240 if (value & v) 241 cnt++; 242 243 Node[] output = new Node[cnt]; 244 cnt = 0; 245 foreach (v; EnumMembers!E) 246 { 247 if (value & v) 248 output[cnt++] = serializeValue!(E)(v); 249 } 250 return Node(output); 251 } 252 else static if (isSimpleList!T) 253 { 254 static if (isRawData!T && !hasAttribute!(AsArrayAttribute, A)) 255 return Node(value); 256 else 257 { 258 Node[] output = new Node[value.length]; 259 alias TV = Unqual!(ForeachType!T); 260 foreach (i, v; value) 261 output[i] = serializeValue!(TV, A)(v); 262 return Node(output); 263 } 264 } 265 else static if (is(TU == struct) || is(TU == class)) 266 { 267 static if (is(T == class)) 268 if (value is null) 269 return Node(); 270 271 static auto safeGetMember(string mname)(ref const T val) @safe 272 { 273 static if (__traits(compiles, __traits(getMember, val, mname))) 274 return __traits(getMember, val, mname); 275 else 276 { 277 pragma(msg, "Warning: Getter for "~fullyQualifiedName!T~"."~mname~" is not @safe"); 278 return () @trusted { return __traits(getMember, val, mname); } (); 279 } 280 } 281 282 static if (hasSerializationMethod!(T, Node)) 283 return __traits(getMember, value, 284 __traits(identifier, serializationMethod!T))(); 285 else static if (hasAttribute!(AsArrayAttribute, A)) 286 { 287 alias members = FilterMembersOf!(TU, isSerializableField); 288 enum nfields = getExpandedFieldCount!(TU, members); 289 Node[] output = new Node[nfields]; 290 size_t fcount = 0; 291 292 foreach (i, mName; members) 293 { 294 alias TMS = TypeTuple!(typeof(__traits(getMember, value, mName))); 295 foreach (j, TM; TMS) 296 { 297 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mName))[j])); 298 static if (!isBuiltinTuple!(T, mName)) 299 output[fcount++] = serializeValue!(TM, TA)(safeGetMember!mName(value)); 300 else 301 output[fcount++] = serializeValue!(TM, TA)(tuple(__traits(getMember, value, mName))[j]); 302 } 303 } 304 return Node(output); 305 } 306 else 307 { 308 Node[string] output; 309 foreach (mName; FilterMembersOf!(TU, isSerializableField)) 310 { 311 alias TM = TypeTuple!(typeof(__traits(getMember, TU, mName))); 312 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mName))[0])); 313 enum memberName = GetMemeberName!(T, mName); 314 static if (!isBuiltinTuple!(T, mName)) 315 auto vt = safeGetMember!mName(value); 316 else 317 { 318 alias TTM = TypeTuple!(typeof(__traits(getMember, value, mName))); 319 auto vt = tuple!TTM(__traits(getMember, value, mName)); 320 } 321 output[memberName] = serializeValue!(typeof(vt), TA)(vt); 322 } 323 return Node(output); 324 } 325 } 326 else static if (isUniNodeInnerType!TU) 327 return Node(value); 328 else 329 static assert(false, "Unsupported serialization type: " ~ T.stringof); 330 } 331 } 332 333 334 /** 335 * Deserializes a value with Serializer 336 */ 337 template deserialize(Node, Serializer : UniNodeSerializer) 338 if (isUniNode!Node) 339 { 340 void deserialize(T)(auto ref Node value, out T result) 341 { 342 static if (__traits(compiles, __traits(getAttributes, T))) 343 { 344 alias TA = TypeTuple!(__traits(getAttributes, T)); 345 result = deserializeValue!(T, TA)(value); 346 } 347 else 348 result = deserializeValue!(T)(value); 349 } 350 351 352 private: 353 354 355 T deserializeValue(T, A...)(auto ref const Node value) 356 { 357 static if (is(T == typeof(null))) 358 return typeof(null).init; 359 else static if (isNullable!T) 360 { 361 if (!value.canNil) 362 return T(deserializeValue!(TemplateArgsOf!T[0], A)(value)); 363 else 364 return T.init; 365 } 366 else static if (isInstanceOf!(Typedef, T)) 367 return T(deserializeValue!(TypedefType!T, A)(value)); 368 else static if (isPointer!T) 369 { 370 if (value.canNil) 371 return null; 372 alias PT = PointerTarget!T; 373 auto ret = new PT; 374 *ret = deserializeValue!(PT, A)(value); 375 return ret; 376 } 377 else static if (isTimeType!T) 378 return T.fromISOExtString(deserializeValue!(string, A)(value)); 379 else static if (isSomeChar!T) 380 { 381 const s = deserializeValue!(string, A)(value); 382 enforceDeserialization(s.length, "String length mismatch"); 383 return s[0]; 384 } 385 else static if (is(T == Tuple!TPS, TPS...)) 386 { 387 import std.algorithm.searching: all; 388 enum fieldsCount = T.Types.length; 389 390 static if (all!"!a.empty"([T.fieldNames]) && !hasAttribute!(AsArrayAttribute, A)) 391 { 392 T output; 393 bool[fieldsCount] set; 394 foreach (ref name, ref const(Node) val; value.getMapping) 395 { 396 switch (name) 397 { 398 foreach (i, TV; T.Types) 399 { 400 enum fieldName = underscoreStrip(T.fieldNames[i]); 401 case fieldName: { 402 output[i] = deserializeValue!(TV, A)(val); 403 set[i] = true; 404 } break; 405 } 406 default: break; 407 } 408 } 409 foreach (i, fieldName; T.fieldNames) 410 enforceDeserialization(set[i], "Missing tuple field '"~fieldName 411 ~"' of type '"~T.Types[i].stringof~"'."); 412 return output; 413 } 414 else static if (fieldsCount == 1) 415 return T(deserializeValue!(T.Types[0], A)(value)); 416 else 417 { 418 T output; 419 size_t currentField = 0; 420 foreach (ref const(Node) val; value.getSequence) 421 { 422 switch (currentField++) 423 { 424 foreach (i, TV; T.Types) 425 { 426 case i: 427 output[i] = deserializeValue!(TV, A)(val); 428 break; 429 } 430 default: break; 431 } 432 } 433 enforceDeserialization(currentField == fieldsCount, 434 "Missing tuple field(s) - expected '"~fieldsCount.stringof 435 ~"', received '"~currentField.stringof~"'."); 436 return output; 437 } 438 } 439 else static if (is(T == BitFlags!E, E)) 440 { 441 T output; 442 foreach (ref idx, ref const(Node) val; value.getSequence) 443 output |= deserializeValue!(E, A)(val); 444 return output; 445 } 446 else static if (is(T == enum)) 447 { 448 static if (hasAttribute!(ByNameAttribyte, A)) 449 return deserializeValue!(string, A)(value).to!T; 450 else 451 return cast(T)deserializeValue!(OriginalType!T, A)(value); 452 } 453 else static if (isStaticArray!T) 454 { 455 alias TV = typeof(T.init[0]); 456 T output; 457 enforceDeserialization(value.length == T.length, "Static array length mismatch"); 458 459 if (value.canRaw) 460 { 461 foreach (ref idx, val; value.get!Bytes) 462 output[idx] = val; 463 } 464 else 465 { 466 foreach (ref idx, ref const(Node) val; value.getSequence) 467 output[idx] = deserializeValue!(TV)(val); 468 } 469 470 return output; 471 } 472 else static if (isSimpleList!T) 473 { 474 alias TV = typeof(T.init[0]); 475 476 T output; 477 output.reserve(value.length); 478 479 if (value.canRaw) 480 { 481 static if (isNumeric!(ForeachType!T)) 482 { 483 foreach (val; value.get!Bytes) 484 output ~= val; 485 } 486 487 return output; 488 } 489 else 490 { 491 foreach (ref idx, ref const(Node) val; value.getSequence) 492 output ~= deserializeValue!(TV)(val); 493 } 494 495 return output; 496 } 497 else static if (isAssociativeArray!T) 498 { 499 alias TK = KeyType!T; 500 alias TV = ValueType!T; 501 T output; 502 503 foreach (ref name, ref const(Node) val; value.getMapping) 504 { 505 TK key; 506 static if (is(TK == string) || (is(TK == enum) 507 && is(OriginalType!TK == string))) 508 key = cast(TK)name; 509 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) 510 key = name.to!TK; 511 else 512 static assert(false, "Associative array keys must be strings," ~ 513 "numbers, enums."); 514 output[key] = deserializeValue!(TV, A)(val); 515 } 516 517 return output; 518 } 519 else static if (is(T : Node)) 520 return value; 521 else static if (is(T == struct) || is(T == class)) 522 { 523 static if (is(T == class)) 524 if (value.canNil) 525 return null; 526 527 void safeSetMember(string mname, U)(ref T value, U fval) @safe 528 { 529 static if (__traits(compiles, () @safe { __traits(getMember, value, mname) = fval; })) 530 __traits(getMember, value, mname) = fval; 531 else 532 { 533 pragma(msg, "Warning: Setter for "~fullyQualifiedName!T~"."~mname~" is not @safe"); 534 () @trusted { __traits(getMember, value, mname) = fval; } (); 535 } 536 } 537 538 bool canDeserializable(A...)(Node val) 539 { 540 static if (hasAttribute!(OptionalAttribute, A)) 541 return !val.canNil(); 542 else 543 return true; 544 } 545 546 static if (hasDeserializationMethod!(T, Node)) 547 return deserializationMethod!T(value); 548 else 549 { 550 T output; 551 static if (is(T == class)) 552 output = new T; 553 alias Members = FilterMembersOf!(T, isDeserializableField); 554 enum FDS = getExpandedFieldsData!(T, Members); 555 bool[FDS.length] set; 556 557 static if (hasAttribute!(AsArrayAttribute, A)) 558 { 559 foreach (ref idx, ref const(Node) val; value.getSequence) 560 { 561 switch (idx) 562 { 563 foreach (i, FD; FDS) 564 { 565 enum mName = FD[0]; 566 enum mIndex = FD[1]; 567 alias MT = TypeTuple!(__traits(getMember, T, mName)); 568 alias MTI = MT[mIndex]; 569 alias TMTI = typeof(MTI); 570 alias TMTIA = TypeTuple!(__traits(getAttributes, MTI)); 571 case i: 572 if (canDeserializable!TMTIA(val)) 573 { 574 static if (!isBuiltinTuple!(T, mName)) 575 safeSetMember!mName(output, deserializeValue!(TMTI, TMTIA)(val)); 576 else 577 __traits(getMember, output, mName)[mIndex] = deserializeValue!(TMTI, 578 TMTIA)(val); 579 } 580 set[i] = true; 581 break; 582 } 583 default: break; 584 } 585 } 586 } 587 else 588 { 589 foreach (ref name, ref const(Node) val; value.getMapping) 590 { 591 switch (name) 592 { 593 foreach (i, mName; Members) 594 { 595 alias TM = TypeTuple!(typeof(__traits(getMember, T, mName))); 596 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, 597 mName))[0])); 598 enum memberName = GetMemeberName!(T, mName); 599 case memberName: 600 if (canDeserializable!TA(val)) 601 { 602 static if (!isBuiltinTuple!(T, mName)) 603 safeSetMember!mName(output, deserializeValue!(TM, TA)(val)); 604 else 605 __traits(getMember, output, mName) = deserializeValue!(Tuple!TM, TA)(val); 606 } 607 set[i] = true; 608 break; 609 } 610 default: break; 611 } 612 } 613 } 614 615 foreach (i, mName; Members) 616 { 617 alias MTA = __traits(getAttributes, __traits(getMember, T, mName)); 618 static if (!hasAttribute!(OptionalAttribute, MTA)) 619 enforceDeserialization(set[i], "Missing non-optional field '"~mName 620 ~"' of type '"~T.stringof~"'."); 621 } 622 623 return output; 624 } 625 } 626 else static if (isUniNodeInnerType!T) 627 { 628 static if (is(T == Node)) 629 return value; 630 else 631 return value.get!T; 632 } 633 else 634 static assert(false, "Unsupported serialization type: " ~ T.stringof); 635 } 636 } 637 638 639 /** 640 * Thrown on UniNode deserialization errors 641 */ 642 class UniNodeDeserializationException : Exception 643 { 644 /** 645 * common constructor 646 */ 647 pure @safe @nogc nothrow 648 this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 649 { 650 super(msg, file, line, next); 651 } 652 } 653 654 655 package alias enforceDeserialization = enforce!UniNodeDeserializationException; 656 657 658 private: 659 660 661 /** 662 * Check nullable Type 663 */ 664 enum isNullable(T) = isInstanceOf!(Nullable, T); 665 666 @("Should work isNullable") 667 @safe unittest 668 { 669 assert (isNullable!(Nullable!int)); 670 assert (isNullable!(Nullable!(int, 0))); 671 assert (!isNullable!int); 672 } 673 674 675 /** 676 * Check datetime type 677 */ 678 template isTimeType(T) { 679 import std.datetime : DateTime, Date, SysTime, TimeOfDay; 680 enum isTimeType = is(T == SysTime) || is(T == DateTime) || is(T == Date) 681 || is(T == TimeOfDay); 682 } 683 684 @("Should work isTimeType") 685 @safe unittest 686 { 687 import std.datetime : DateTime, Date, SysTime, TimeOfDay; 688 assert (isTimeType!DateTime); 689 assert (isTimeType!Date); 690 assert (isTimeType!SysTime); 691 assert (isTimeType!TimeOfDay); 692 } 693 694 695 /** 696 * Determines if a member is a public, non-static, de-facto data field. 697 * In addition to plain data fields, R/W properties are also accepted. 698 */ 699 template isSerializableAvailableField(T, string M) 700 { 701 import std.typetuple : TypeTuple; 702 703 static void testAssign()() 704 { 705 T t = void; 706 __traits(getMember, t, M) = __traits(getMember, t, M); 707 } 708 709 // reject type aliases 710 static if (is(TypeTuple!(__traits(getMember, T, M)))) 711 enum isSerializableAvailableField = false; 712 // reject non-public members 713 else static if (!isPublicMember!(T, M)) 714 enum isSerializableAvailableField = false; 715 // reject static members 716 else static if (!isNonStaticMember!(T, M)) 717 enum isSerializableAvailableField = false; 718 // reject non-typed members 719 else static if (!is(typeof(__traits(getMember, T, M)))) 720 enum isSerializableAvailableField = false; 721 // reject void typed members (includes templates) 722 else static if (is(typeof(__traits(getMember, T, M)) == void)) 723 enum isSerializableAvailableField = false; 724 // reject non-assignable members 725 else static if (!__traits(compiles, testAssign!()())) 726 enum isSerializableAvailableField = false; 727 // reject ignore members 728 else static if (anySatisfy!(isSomeFunction, __traits(getMember, T, M))) 729 { 730 // If M is a function, reject if not @property or returns by ref 731 private enum FA = functionAttributes!(__traits(getMember, T, M)); 732 enum isSerializableAvailableField = (FA & FunctionAttribute.property) != 0; 733 } 734 else 735 enum isSerializableAvailableField = true; 736 } 737 738 739 /** 740 * Determins if a member is a public, non-static data field. 741 */ 742 template isSerializablePlainField(T, string M) 743 { 744 private T tGen(){ return T.init; } 745 746 static if (!isSerializableField!(T, M)) 747 enum isSerializablePlainField = false; 748 else 749 { 750 enum isSerializablePlainField = __traits(compiles, 751 *(&__traits(getMember, tGen(), M)) = *(&__traits(getMember, tGen(), M))); 752 } 753 } 754 755 756 /** 757 * Determins if a member is serializable 758 */ 759 template isSerializableField(T, string M) 760 { 761 static if (isSerializableAvailableField!(T, M)) 762 { 763 alias AA = __traits(getAttributes, __traits(getMember, T, M)); 764 static if (hasAttribute!(MaskedAttribute, AA)) 765 enum isSerializableField = false; 766 else static if (hasAttribute!(IgnoreAttribute, AA)) 767 enum isSerializableField = false; 768 else 769 enum isSerializableField = true; 770 } 771 else 772 enum isSerializableField = false; 773 } 774 775 776 /** 777 * Determins if a member is deseriazable 778 */ 779 template isDeserializableField(T, string M) 780 { 781 static if (isSerializableAvailableField!(T, M)) 782 { 783 alias AA = __traits(getAttributes, __traits(getMember, T, M)); 784 static if (hasAttribute!(IgnoreAttribute, AA)) 785 enum isDeserializableField = false; 786 else static if (hasAttribute!(AsStringAttribute, AA)) 787 enum isDeserializableField = false; 788 else 789 enum isDeserializableField = true; 790 } 791 else 792 enum isDeserializableField = false; 793 } 794 795 796 @("Should work isSerializableAvailableField") 797 @safe unittest 798 { 799 import std.algorithm.searching : canFind; 800 801 struct S 802 { 803 alias a = int; // alias 804 int i; // plain field 805 enum j = 42; // manifest constant 806 static int k = 42; // static field 807 private int privateJ; //private field 808 809 this(Args...)(Args args) {} 810 811 // read-write property (OK) 812 @property int p1() { return privateJ; } 813 @property void p1(int j) { privateJ = j; } 814 // read-only property (NO) 815 @property int p2() { return privateJ; } 816 // write-only property (NO) 817 @property void p3(int value) { privateJ = value; } 818 // ref returning property (OK) 819 @property ref int p4() { return i; } 820 // parameter-less template property (OK) 821 @property ref int p5()() { return i; } 822 // not treated as a property by DMD, so not a field 823 @property int p6()() { return privateJ; } 824 @property void p6(int j)() { privateJ = j; } 825 826 static @property int p7() { return k; } 827 static @property void p7(int value) { k = value; } 828 829 ref int f1() { return i; } // ref returning function (no field) 830 831 int f2(Args...)(Args) { return i; } 832 833 ref int f3(Args...)(Args) { return i; } 834 835 void someMethod() {} 836 837 ref int someTempl()() { return i; } 838 } 839 840 static immutable plainFields = ["i"]; 841 static immutable fields = ["i", "p1", "p4", "p5"]; 842 843 foreach (fName; __traits(allMembers, S)) 844 { 845 static if (isSerializableField!(S, fName)) 846 assert (fields.canFind(fName), fName ~ " detected as field."); 847 else 848 assert (!fields.canFind(fName), fName ~ " not detected as field."); 849 850 static if (isSerializablePlainField!(S, fName)) 851 assert (plainFields.canFind(fName), fName ~ " not detected as plain field."); 852 else 853 assert (!plainFields.canFind(fName), fName ~ " not detected as plain field."); 854 } 855 } 856 857 858 /** 859 * Tests if a member requires $(D this) to be used. 860 */ 861 template isNonStaticMember(T, string M) 862 { 863 import std.typetuple; 864 import std.traits; 865 866 static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) 867 enum isNonStaticMember = false; 868 else 869 { 870 alias MF = TypeTuple!(__traits(getMember, T, M)); 871 static if (M.length == 0) 872 enum isNonStaticMember = false; 873 else static if (anySatisfy!(isSomeFunction, MF)) 874 enum isNonStaticMember = !__traits(isStaticFunction, MF); 875 else 876 enum isNonStaticMember = !__traits(compiles, (){ auto x = __traits(getMember, T, M); }()); 877 } 878 } 879 880 @("Should work isNonStaticMember template") 881 @safe unittest 882 { 883 struct S 884 { 885 int a; 886 static int b; 887 enum c = 42; 888 void f(); 889 static void g(); 890 ref int h() { return a; } 891 static ref int i() { return b; } 892 } 893 894 assert (isNonStaticMember!(S, "a")); 895 assert (!isNonStaticMember!(S, "b")); 896 assert (!isNonStaticMember!(S, "c")); 897 assert (isNonStaticMember!(S, "f")); 898 assert (!isNonStaticMember!(S, "g")); 899 assert (isNonStaticMember!(S, "h")); 900 assert (!isNonStaticMember!(S, "i")); 901 } 902 903 @("Should work isNonStaticMember tuple fields") 904 @safe unittest 905 { 906 struct S(T...) 907 { 908 T a; 909 static T b; 910 } 911 912 alias T = S!(int, float); 913 assert (isNonStaticMember!(T, "a")); 914 assert (!isNonStaticMember!(T, "b")); 915 916 alias U = S!(); 917 assert (!isNonStaticMember!(U, "a")); 918 assert (!isNonStaticMember!(U, "b")); 919 } 920 921 922 /** 923 * Tests if the protection of a member is public. 924 */ 925 template isPublicMember(T, string M) 926 { 927 import std.algorithm : among; 928 929 static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) 930 enum isPublicMember = false; 931 else 932 { 933 alias MEM = TypeTuple!(__traits(getMember, T, M)); 934 enum isPublicMember = __traits(getProtection, MEM).among("public", "export"); 935 } 936 } 937 938 @("Should work isPublicMember") 939 @safe unittest 940 { 941 class C 942 { 943 int a; 944 export int b; 945 protected int c; 946 private int d; 947 package int e; 948 void f() {} 949 static void g() {} 950 private void h() {} 951 private static void i() {} 952 } 953 954 assert (isPublicMember!(C, "a")); 955 assert (isPublicMember!(C, "b")); 956 assert (!isPublicMember!(C, "c")); 957 assert (!isPublicMember!(C, "d")); 958 assert (!isPublicMember!(C, "e")); 959 assert (isPublicMember!(C, "f")); 960 assert (isPublicMember!(C, "g")); 961 assert (!isPublicMember!(C, "h")); 962 assert (!isPublicMember!(C, "i")); 963 964 struct S 965 { 966 int a; 967 export int b; 968 private int d; 969 package int e; 970 } 971 assert (isPublicMember!(S, "a")); 972 assert (isPublicMember!(S, "b")); 973 assert (!isPublicMember!(S, "d")); 974 assert (!isPublicMember!(S, "e")); 975 976 S s; 977 s.a = 21; 978 assert (s.a == 21); 979 } 980 981 982 /** 983 * Check Tuple 984 */ 985 template isBuiltinTuple(T, string member) 986 { 987 alias TM = AliasSeq!(typeof(__traits(getMember, T.init, member))); 988 static if (TM.length > 1) enum isBuiltinTuple = true; 989 else static if (is(typeof(__traits(getMember, T.init, member)) == TM[0])) 990 enum isBuiltinTuple = false; 991 else enum isBuiltinTuple = true; // single-element tuple 992 } 993 994 995 /** 996 * Get expanded fields count 997 */ 998 size_t getExpandedFieldCount(T, FIELDS...)() 999 { 1000 size_t ret = 0; 1001 foreach (F; FIELDS) 1002 ret += TypeTuple!(__traits(getMember, T, F)).length; 1003 return ret; 1004 } 1005 1006 1007 /** 1008 * Get expanded fields data 1009 */ 1010 template getExpandedFieldsData(T, FIELDS...) 1011 { 1012 import std.meta : aliasSeqOf, staticMap; 1013 import std.range : repeat, zip, iota; 1014 1015 enum subfieldsCount(alias F) = TypeTuple!(__traits(getMember, T, F)).length; 1016 alias processSubfield(alias F) = aliasSeqOf!(zip(repeat(F), iota(subfieldsCount!F))); 1017 alias getExpandedFieldsData = staticMap!(processSubfield, FIELDS); 1018 } 1019 1020 1021 /** 1022 * Strip underscore 1023 */ 1024 string underscoreStrip(string fieldName) @safe nothrow @nogc 1025 { 1026 if (fieldName.length < 1 || fieldName[$-1] != '_') 1027 return fieldName; 1028 else 1029 return fieldName[0 .. $-1]; 1030 } 1031 1032 1033 /** 1034 * Return member name 1035 */ 1036 template GetMemeberName(T, string M) 1037 { 1038 alias isNamed(alias M) = hasUDA!(M, NameAttribute); 1039 alias FM = Filter!(isNamed, TypeTuple!(__traits(getMember, T, M))); 1040 static if (FM.length > 0) 1041 enum GetMemeberName = underscoreStrip(getUDAs!(FM[0], NameAttribute)[0].name); 1042 else 1043 enum GetMemeberName = underscoreStrip(M); 1044 } 1045 1046 1047 /** 1048 * Check attribute 1049 */ 1050 template hasAttribute(alias T, ATTRIBUTES...) 1051 { 1052 static if (ATTRIBUTES.length == 1) 1053 enum hasAttribute = is(typeof(ATTRIBUTES[0]) == T); 1054 else static if (ATTRIBUTES.length > 1) 1055 enum hasAttribute = hasAttribute!(T, ATTRIBUTES[0 .. $/2]) || hasAttribute!(T, ATTRIBUTES[$/2 .. $]); 1056 else 1057 enum hasAttribute = false; 1058 } 1059 1060 1061 /** 1062 * Return serialization method 1063 */ 1064 alias serializationMethod(T) = getSymbolsByUDA!(T, SerializationMethod)[0]; 1065 1066 1067 /** 1068 * Check SerializationMethod 1069 */ 1070 template hasSerializationMethod(T, Node) 1071 { 1072 alias methods = getSymbolsByUDA!(T, SerializationMethod); 1073 1074 static if (methods.length == 1) 1075 enum hasSerializationMethod = Parameters!(methods[0]).length == 0 1076 && is(ReturnType!(methods[0]) : Node); 1077 else 1078 enum hasSerializationMethod = false; 1079 } 1080 1081 1082 /** 1083 * Return deserialization method 1084 */ 1085 alias deserializationMethod(T) = getSymbolsByUDA!(T, DeserializationMethod)[0]; 1086 1087 1088 /** 1089 * Check SerializationMethod 1090 */ 1091 template hasDeserializationMethod(T, Node) 1092 { 1093 alias methods = getSymbolsByUDA!(T, DeserializationMethod); 1094 1095 static if (methods.length == 1) 1096 enum hasDeserializationMethod = Parameters!(methods[0]).length == 1 1097 && is(Parameters!(methods[0])[0] : Node) 1098 && is(ReturnType!(methods[0]) == T); 1099 else 1100 enum hasDeserializationMethod = false; 1101 } 1102 1103 1104 /** 1105 * Check type of simple list 1106 */ 1107 alias isSimpleList = templateAnd!(isArray, templateNot!isSomeString, 1108 templateNot!isAssociativeArray); 1109 1110 @("Should work isSimpleList") 1111 @safe unittest 1112 { 1113 assert(isSimpleList!(int[])); 1114 assert(isSimpleList!(string[])); 1115 assert(!isSimpleList!(string)); 1116 assert(!isSimpleList!(char[])); 1117 assert(!isSimpleList!(int)); 1118 assert(!isSimpleList!(int[string])); 1119 assert(isSimpleList!(char[10])); 1120 } 1121 1122 1123 /** 1124 * Attribute for overriding the field name during (de-)serialization. 1125 */ 1126 struct NameAttribute 1127 { 1128 /// Custom member name 1129 string name; 1130 } 1131 1132 1133 /** 1134 * Attribute for forcing serialization of enum fields by name instead of by value. 1135 */ 1136 struct ByNameAttribyte {} 1137 1138 1139 /** 1140 * Attribute for representing a struct/class as an array instead of an object. 1141 */ 1142 struct AsArrayAttribute {} 1143 1144 1145 /** 1146 * Attribute marking a field as optional during deserialization. 1147 */ 1148 struct OptionalAttribute {} 1149 1150 1151 /** 1152 * Attribute marking a field as masked during serialization. 1153 */ 1154 struct MaskedAttribute {} 1155 1156 1157 /** 1158 * Attribute for marking non-serialized fields. 1159 */ 1160 struct IgnoreAttribute {} 1161 1162 1163 /** 1164 * Attribute for forcing serialization as string. 1165 */ 1166 struct AsStringAttribute {} 1167