1 /++ 2 $(H4 High level serialization API) 3 4 Macros: 5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP) 6 Authros: Ilia Ki 7 +/ 8 module mir.ser; 9 10 import mir.conv; 11 import mir.deser; 12 import mir.ion.internal.basic_types; 13 import mir.ion.type_code; 14 import mir.reflection; 15 import std.meta; 16 import std.traits; 17 import mir.internal.meta: hasUDA, getUDAs; 18 import mir.exception: toMutable; 19 20 public import mir.serde; 21 22 static immutable cannotSerializeVoidMsg = "Can't serialize none (void) value of the algebraic type"; 23 version (D_Exceptions) 24 private static immutable cannotSerializeVoid = new Exception(cannotSerializeVoidMsg); 25 26 private noreturn serializeVoidHandler() @safe pure @nogc 27 { 28 version (D_Exceptions) 29 throw cannotSerializeVoid.toMutable; 30 else 31 assert(0, cannotSerializeVoidMsg); 32 } 33 34 private noreturn serializeVoidHandlerWithSerializer(S)(scope ref S serializer) @safe pure @nogc 35 { 36 version (D_Exceptions) 37 throw cannotSerializeVoid.toMutable; 38 else 39 assert(0, cannotSerializeVoidMsg); 40 } 41 42 private noreturn serializeVoidHandlerWithSerializerAndState(S)(scope ref S serializer, size_t state) @safe pure @nogc 43 { 44 version (D_Exceptions) 45 throw cannotSerializeVoid.toMutable; 46 else 47 assert(0, cannotSerializeVoidMsg); 48 } 49 50 /// `null` value serialization 51 void serializeValue(S)(scope ref S serializer, typeof(null)) 52 { 53 serializer.putValue(null); 54 } 55 56 /// 57 version(mir_ion_test) 58 unittest 59 { 60 import mir.ser.json: serializeJson; 61 assert(serializeJson(null) == `null`, serializeJson(null)); 62 } 63 64 /// Number serialization 65 void serializeValue(S, V)(scope ref S serializer, const V value) 66 if (isNumeric!V && !is(V == enum)) 67 { 68 serializer.putValue(value); 69 } 70 71 /// 72 version(mir_ion_test) 73 unittest 74 { 75 import mir.ser.json: serializeJson; 76 77 assert(serializeJson(2.40f) == `2.4`); 78 assert(serializeJson(float.nan) == `"nan"`); 79 assert(serializeJson(float.infinity) == `"+inf"`); 80 assert(serializeJson(-float.infinity) == `"-inf"`); 81 } 82 83 /// Boolean serialization 84 void serializeValue(S, V)(scope ref S serializer, scope const V value) 85 if (is(V == bool) && !is(V == enum)) 86 { 87 serializer.putValue(value); 88 } 89 90 /// Char serialization 91 void serializeValue(S, V : char)(scope ref S serializer, scope const V value) 92 if (is(V == char) && !is(V == enum)) 93 { 94 char[1] v = value; 95 serializer.putValue(v[]); 96 } 97 98 /// 99 version(mir_ion_test) 100 unittest 101 { 102 import mir.ser.json: serializeJson; 103 assert(serializeJson(true) == `true`); 104 } 105 106 /// Enum serialization 107 void serializeValue(S, V)(scope ref S serializer, scope const V value) 108 if(is(V == enum)) 109 { 110 static if (hasUDA!(V, serdeProxy)) 111 { 112 serializeProxyCastImpl!(S, V)(serializer, value); 113 } 114 else 115 { 116 serializer.putSymbol(serdeGetKeyOut(value)); 117 } 118 } 119 120 /// 121 version(mir_ion_test) 122 unittest 123 { 124 import mir.ser.json: serializeJson; 125 import mir.ser.ion: serializeIon; 126 import mir.ser.text: serializeText; 127 import mir.deser.ion: deserializeIon; 128 import mir.small_string; 129 import mir.rc.array; 130 enum Key { bar, @serdeKeys("FOO", "foo") foo } 131 assert(serializeJson(Key.foo) == `"FOO"`); 132 assert(serializeText(Key.foo) == `FOO`); 133 assert(serializeIon(Key.foo).deserializeIon!Key == Key.foo); 134 assert(serializeIon(Key.foo).deserializeIon!string == "FOO"); 135 assert(serializeIon(Key.foo).deserializeIon!(SmallString!32) == "FOO"); 136 auto rcstring = serializeIon(Key.foo).deserializeIon!(RCArray!char); 137 assert(rcstring[] == "FOO"); 138 } 139 140 private static void serializeProxyCastImpl(S, alias U, V)(scope ref S serializer, scope const V value) 141 { 142 static if (hasUDA!(U, serdeProxyCast)) 143 { 144 scope casted = cast(serdeGetProxy!U)value; 145 serializeValue(serializer, casted); 146 } 147 else 148 serializer.serializeWithProxy!(serdeGetProxy!U)(value); 149 } 150 151 /// String serialization 152 void serializeValue(S)(scope ref S serializer, scope const(char)[] value) 153 { 154 if(value is null) 155 { 156 serializer.putNull(IonTypeCode..string); 157 return; 158 } 159 serializer.putValue(value); 160 } 161 162 /// 163 version(mir_ion_test) 164 unittest 165 { 166 import mir.ser.json: serializeJson; 167 assert(serializeJson("\t \" \\") == `"\t \" \\"`, serializeJson("\t \" \\")); 168 } 169 170 /// Array serialization 171 void serializeValue(S, T)(scope ref S serializer, scope const T[] value) @safe 172 if(!isSomeChar!T) 173 { 174 if (value is null) 175 { 176 serializer.putNull(IonTypeCode.list); 177 return; 178 } 179 auto state = serializer.beginList(value); 180 foreach (ref elem; value) 181 { 182 serializer.elemBegin; 183 serializer.serializeValue(elem); 184 } 185 serializer.listEnd(state); 186 } 187 188 private enum isRefIterable(T) = __traits(compiles, (ref T value) { foreach (ref elem; value) {} }); 189 190 version(mir_ion_test) 191 unittest 192 { 193 static assert(isRefIterable!(double[])); 194 } 195 196 package template hasLikeList(T) 197 { 198 import mir.serde: serdeLikeList; 199 static if (is(T == enum) || isAggregateType!T) 200 enum hasLikeList = hasUDA!(T, serdeLikeList); 201 else 202 enum hasLikeList = false; 203 } 204 205 package template hasProxy(T) 206 { 207 import mir.serde: serdeProxy; 208 static if (is(T == enum) || isAggregateType!T) 209 enum hasProxy = hasUDA!(T, serdeProxy); 210 else 211 enum hasProxy = false; 212 } 213 214 package template hasFields(T) 215 { 216 import mir.serde: serdeFields; 217 static if (is(T == enum) || isAggregateType!T) 218 enum hasFields = hasUDA!(T, serdeFields); 219 else 220 enum hasFields = false; 221 } 222 223 224 /// input range serialization 225 version(mir_ion_test) 226 unittest 227 { 228 import mir.algorithm.iteration : filter; 229 230 static struct Foo 231 { 232 int i; 233 } 234 235 auto ar = [Foo(1), Foo(3), Foo(4), Foo(17)]; 236 237 auto filtered1 = ar.filter!"a.i & 1"; 238 auto filtered2 = ar.filter!"!(a.i & 1)"; 239 240 import mir.ser.json: serializeJson; 241 assert(serializeJson(filtered1) == `[{"i":1},{"i":3},{"i":17}]`); 242 assert(serializeJson(filtered2) == `[{"i":4}]`); 243 } 244 245 /// 246 unittest 247 { 248 import mir.ser.json: serializeJson; 249 uint[2] ar = [1, 2]; 250 assert(serializeJson(ar) == `[1,2]`); 251 assert(serializeJson(ar[]) == `[1,2]`); 252 assert(serializeJson(ar[0 .. 0]) == `[]`); 253 assert(serializeJson((uint[]).init) == `[]`); 254 } 255 256 /// String-value associative array serialization 257 void serializeValue(S, T)(scope ref S serializer, scope const T[string] value) 258 { 259 if(value is null) 260 { 261 serializer.putNull(IonTypeCode.struct_); 262 return; 263 } 264 auto state = serializer.beginStruct(value); 265 foreach (key, ref const val; (()@trusted => cast()value)()) 266 { 267 serializer.putKey(key); 268 serializer.serializeValue(val); 269 } 270 serializer.structEnd(state); 271 } 272 273 /// 274 version(mir_ion_test) 275 unittest 276 { 277 import mir.ser.json: serializeJson; 278 import mir.ser.text: serializeText; 279 uint[string] ar = ["a" : 1]; 280 auto car = cast(const)ar; 281 assert(serializeJson(car) == `{"a":1}`); 282 assert(serializeText(ar) == `{a:1}`); 283 ar.remove("a"); 284 assert(serializeJson(ar) == `{}`); 285 assert(serializeJson((uint[string]).init) == `{}`); 286 } 287 288 /// Enumeration-value associative array serialization 289 void serializeValue(S, V : const T[K], T, K)(scope ref S serializer, scope const V value) 290 if(is(K == enum)) 291 { 292 if(value is null) 293 { 294 serializer.putNull(IonTypeCode.struct_); 295 return; 296 } 297 auto state = serializer.beginStruct(value); 298 foreach (key, ref val; value) 299 { 300 serializer.putKey(serdeGetKeyOut(key)); 301 serializer.serializeValue(val); 302 } 303 serializer.structEnd(state); 304 } 305 306 /// 307 version(mir_ion_test) 308 unittest 309 { 310 import mir.ser.json: serializeJson; 311 enum E { a, b } 312 uint[E] ar = [E.a : 1]; 313 auto car = cast(const)ar; 314 assert(serializeJson(car) == `{"a":1}`); 315 ar.remove(E.a); 316 assert(serializeJson(ar) == `{}`); 317 assert(serializeJson((uint[string]).init) == `{}`); 318 } 319 320 /// integral typed value associative array serialization 321 void serializeValue(S, V : const T[K], T, K)(scope ref S serializer, scope const V value) 322 if (isIntegral!K && !is(K == enum)) 323 { 324 if(value is null) 325 { 326 serializer.putNull(IonTypeCode.struct_); 327 return; 328 } 329 auto state = serializer.beginStruct(value); 330 foreach (key, ref val; value) 331 { 332 import mir.format: print, stringBuf; 333 import mir.small_string : SmallString; 334 auto buffer = stringBuf; 335 print(buffer, key); 336 serializer.putKey(buffer.data); 337 .serializeValue(serializer, val); 338 } 339 serializer.structEnd(state); 340 } 341 342 /// 343 version(mir_ion_test) 344 unittest 345 { 346 import mir.ser.json: serializeJson; 347 uint[short] ar = [256 : 1]; 348 assert(serializeJson(ar) == `{"256":1}`); 349 ar.remove(256); 350 assert(serializeJson(ar) == `{}`); 351 assert(serializeJson((uint[string]).init) == `{}`); 352 // assert(deserializeJson!(uint[short])(`{"256":1}`) == cast(uint[short]) [256 : 1]); 353 } 354 355 356 private IonTypeCode nullTypeCodeOf(T)() 357 { 358 import mir.algebraic: isVariant; 359 import mir.serde: serdeGetFinalProxy; 360 import std.traits: Unqual; 361 362 IonTypeCode code; 363 364 alias U = Unqual!T; 365 366 static if (is(U == bool)) 367 code = IonTypeCode.bool_; 368 else 369 static if (isUnsigned!U) 370 code = IonTypeCode.uInt; 371 else 372 static if (isIntegral!U || isBigInt!U) 373 code = IonTypeCode.nInt; 374 else 375 static if (isFloatingPoint!U) 376 code = IonTypeCode.float_; 377 else 378 static if (isDecimal!U) 379 code = IonTypeCode.decimal; 380 else 381 static if (isTuple!U) 382 code = IonTypeCode.list; 383 else 384 static if (isClob!U) 385 code = IonTypeCode.clob; 386 else 387 static if (isBlob!U) 388 code = IonTypeCode.blob; 389 else 390 static if (isSomeString!U) 391 code = IonTypeCode..string; 392 else 393 static if (!isVariant!U && isAggregateType!U) 394 { 395 static if (hasUDA!(U, serdeProxy)) 396 code = .nullTypeCodeOf!(serdeGetFinalProxy!U); 397 else 398 static if (isIterable!U && !hasFields!U) 399 code = IonTypeCode.list; 400 else 401 code = IonTypeCode.struct_; 402 } 403 else 404 static if (isIterable!U && !hasFields!U) 405 code = IonTypeCode.list; 406 407 return code; 408 } 409 410 version(mir_ion_test) 411 unittest 412 { 413 static assert(nullTypeCodeOf!long == IonTypeCode.nInt); 414 } 415 416 @safe 417 private void serializeAnnotatedValue(S, V)(scope ref S serializer, scope ref const V value, size_t wrapperState) 418 { 419 import mir.algebraic: Algebraic; 420 static if (serdeGetAnnotationMembersOut!V.length) 421 { 422 static foreach (annotationMember; serdeGetAnnotationMembersOut!V) 423 { 424 static if (is(typeof(__traits(getMember, value, annotationMember)) == enum)) 425 serializer.putAnnotation(serdeGetKeyOut(__traits(getMember, value, annotationMember))); 426 else 427 static if (__traits(compiles, serializer.putAnnotation(__traits(getMember, value, annotationMember)[]))) 428 serializer.putAnnotation(__traits(getMember, value, annotationMember)[]); 429 else 430 foreach (annotation; __traits(getMember, value, annotationMember)) 431 serializer.putAnnotation(annotation); 432 } 433 } 434 435 static if (isAlgebraicAliasThis!V || isAnnotated!V) 436 { 437 static if (__traits(getAliasThis, V).length == 1) 438 enum aliasThisMember = __traits(getAliasThis, V)[0]; 439 else 440 enum aliasThisMember = "value"; 441 return serializeAnnotatedValue(serializer, __traits(getMember, value, aliasThisMember), wrapperState); 442 } 443 else 444 static if (is(V == Algebraic!TypeSet, TypeSet...) && (!isStdNullable!V || Algebraic!TypeSet.AllowedTypes.length != 2)) 445 { 446 import mir.algebraic: match; 447 match!( 448 serializeVoidHandlerWithSerializerAndState, 449 staticMap!(serializeAlgebraicAnnotationContinue!S, Filter!(serdeHasAlgebraicAnnotation, V.AllowedTypes)), 450 serializeAnnotatedValue, 451 )(serializer, value, wrapperState); 452 } 453 else 454 { 455 auto annotationsState = serializer.annotationsEnd(wrapperState); 456 static if (serdeGetAnnotationMembersOut!V.length) 457 serializeValueImpl(serializer, value); 458 else 459 serializeValue(serializer, value); 460 serializer.annotationWrapperEnd(annotationsState, wrapperState); 461 } 462 } 463 464 /// Struct and class type serialization 465 private void serializeValueImpl(S, V)(scope ref S serializer, scope ref const V value) 466 if (isAggregateType!V && (!isIterable!V || hasFields!V || hasUDA!(V, serdeProxy) && !hasUDA!(V, serdeLikeList))) 467 { 468 import mir.algebraic; 469 auto state = serializer.structBegin; 470 471 static if (hasUDA!(V, serdeDiscriminatedField)) 472 {{ 473 enum udas = getUDAs!(V, serdeDiscriminatedField); 474 static assert (udas.length == 1); 475 enum key = udas[0].field; 476 477 static if (__traits(hasMember, S, "putCompiletimeKey")) 478 { 479 serializer.putCompiletimeKey!key; 480 } 481 else 482 static if (__traits(hasMember, S, "putKeyPtr")) 483 { 484 serializer.putKeyPtr(key.ptr); 485 } 486 else 487 { 488 serializer.putKey(key); 489 } 490 491 serializer.putSymbol(udas[0].tag); 492 }} 493 494 foreach(member; aliasSeqOf!(SerializableMembers!V)) 495 {{ 496 enum key = serdeGetKeyOut!(__traits(getMember, value, member)); 497 498 static if (key !is null) 499 { 500 static if (hasUDA!(__traits(getMember, value, member), serdeIgnoreDefault)) 501 { 502 if (__traits(getMember, value, member) == __traits(getMember, V.init, member)) 503 continue; 504 } 505 506 static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreOutIf)) 507 { 508 alias pred = serdeGetIgnoreOutIf!(__traits(getMember, value, member)); 509 if (pred(__traits(getMember, value, member))) 510 continue; 511 } 512 513 static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreIfAggregate)) 514 { 515 alias pred = serdeGetIgnoreIfAggregate!(__traits(getMember, value, member)); 516 if (pred(value)) 517 continue; 518 } 519 520 static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreOutIfAggregate)) 521 { 522 alias pred = serdeGetIgnoreOutIfAggregate!(__traits(getMember, value, member)); 523 if (pred(value)) 524 continue; 525 } 526 527 static if(__traits(hasMember, __traits(getMember, value, member), "serdeIgnoreOut")) 528 { 529 if (__traits(getMember, __traits(getMember, value, member), "serdeIgnoreOut")) 530 continue; 531 } 532 533 static if(__traits(hasMember, __traits(getMember, value, member), "_void")) 534 { 535 if (__traits(getMember, value, member)._is!void) 536 continue; 537 } 538 539 static if(hasUDA!(__traits(getMember, value, member), serdeTransformOut)) 540 { 541 alias f = serdeGetTransformOut!(__traits(getMember, value, member)); 542 auto val = f(__traits(getMember, value, member)); 543 alias W = typeof(val); 544 } 545 else 546 static if (hasField!(V, member)) 547 { 548 scope valPtr = (()@trusted => &__traits(getMember, value, member))(); 549 alias W = typeof(*valPtr); 550 ref W val() @trusted pure @property { return *valPtr; } 551 } 552 else 553 { 554 auto val = __traits(getMember, value, member); 555 alias W = typeof(val); 556 } 557 558 static if (__traits(hasMember, S, "putCompiletimeKey")) 559 { 560 serializer.putCompiletimeKey!key; 561 } 562 else 563 static if (__traits(hasMember, S, "putKeyPtr")) 564 { 565 serializer.putKeyPtr((()@trusted => key.ptr)()); 566 } 567 else 568 { 569 serializer.putKey(key); 570 } 571 572 static if(hasUDA!(__traits(getMember, value, member), serdeLikeList)) 573 { 574 static assert(0); 575 } 576 else 577 static if(hasUDA!(__traits(getMember, value, member), serdeLikeStruct)) 578 { 579 static if(is(W == interface) || is(W == class) || is(W : E[T], E, T)) 580 { 581 if(val is null) 582 { 583 serializer.putNull(IonTypeCode.struct_); 584 continue F; 585 } 586 } 587 auto valState = serializer.beginStruct(val); 588 static if (__traits(compiles, {foreach (keyElem; val.byKeyValue){}})) 589 { 590 foreach (keyElem; val.byKeyValue) 591 { 592 serializer.putKey(keyElem.key); 593 serializer.serializeValue(keyElem.value); 594 } 595 } 596 else 597 { 598 foreach (key, ref elem; val) 599 { 600 serializer.putKey(key); 601 serializer.serializeValue(elem); 602 } 603 } 604 serializer.structEnd(valState); 605 } 606 else 607 static if(hasUDA!(__traits(getMember, value, member), serdeProxy)) 608 { 609 serializeProxyCastImpl!(S, __traits(getMember, value, member))(serializer, val); 610 } 611 else 612 { 613 serializer.serializeValue(val); 614 } 615 } 616 }} 617 static if(__traits(hasMember, V, "finalizeSerialization")) 618 { 619 value.finalizeSerialization(serializer); 620 } 621 622 serializer.structEnd(state); 623 } 624 625 private template serializeWithProxy(Proxy) 626 { 627 @safe 628 void serializeWithProxy(S, V)(scope ref S serializer, scope ref const V value) 629 { 630 static if (is(Proxy == const(char)[]) || is(Proxy == string) || is(Proxy == char[])) 631 { 632 import mir.format: stringBuf, print, getData; 633 static if (__traits(compiles, serializeValue(serializer, stringBuf() << value << getData))) 634 serializeValue(serializer, stringBuf() << value << getData); 635 else 636 { 637 serializeValue(serializer, to!Proxy(value)); 638 } 639 } 640 else 641 { 642 static if (isImplicitlyConvertible!(const V, const Proxy)) 643 { 644 scope const Proxy proxy = value; 645 serializeValue(serializer, proxy); 646 } 647 else 648 { 649 static if (is(typeof(()@safe{return to!(const Proxy)(value);}))) 650 auto proxy = to!(const Proxy)(value); 651 else 652 { 653 pragma(msg, "Mir warning: can't safely cast from ", (const V).stringof, " to ", (const Proxy).stringof 654 ); 655 auto proxy = ()@trusted{return to!(const Proxy)(value);}(); 656 } 657 serializeValue(serializer, proxy); 658 } 659 } 660 } 661 } 662 663 private template serializeAlgebraicAnnotation(S) 664 { 665 @safe 666 void serializeAlgebraicAnnotation(V)(scope ref S serializer, scope ref const V value) 667 if (serdeHasAlgebraicAnnotation!V) 668 { 669 auto wrapperState = serializer.annotationWrapperBegin; 670 alias continueThis = serializeAlgebraicAnnotationContinue!S; 671 return continueThis(serializer, value, wrapperState); 672 } 673 } 674 675 private template serializeAlgebraicAnnotationContinue(S) 676 { 677 void serializeAlgebraicAnnotationContinue(V)(scope ref S serializer, scope ref const V value, size_t wrapperState) 678 if (serdeHasAlgebraicAnnotation!V) 679 { 680 static if (__traits(hasMember, S, "putCompiletimeAnnotation")) 681 serializer.putCompiletimeAnnotation!(serdeGetAlgebraicAnnotation!V); 682 else 683 static if (__traits(hasMember, S, "putAnnotationPtr")) 684 serializer.putAnnotationPtr((()@trusted =>serdeGetAlgebraicAnnotation!V.ptr)()); 685 else 686 serializer.putAnnotation(serdeGetAlgebraicAnnotation!V); 687 serializeAnnotatedValue(serializer, value, wrapperState); 688 } 689 } 690 691 /// Struct and class type serialization 692 void serializeValue(S, V)(scope ref S serializer, scope ref const V value) @safe 693 if (isAggregateType!V) 694 { 695 import mir.algebraic: Algebraic, isVariant, isNullable; 696 import mir.string_map: isStringMap; 697 import mir.timestamp: Timestamp; 698 699 static if(is(V == class) || is(V == interface)) 700 { 701 if(value is null) 702 { 703 serializer.putNull(nullTypeCodeOf!V); 704 return; 705 } 706 } 707 708 static if (isBigInt!V || isDecimal!V || isTimestamp!V || isBlob!V || isClob!V) 709 { 710 serializer.putValue(value); 711 return; 712 } 713 else 714 static if (isTuple!V) 715 { 716 auto state = serializer.listBegin(value.expand.length); 717 foreach (ref v; value.expand) 718 { 719 serializer.elemBegin; 720 .serializeValue(serializer, v); 721 } 722 serializer.listEnd(state); 723 } 724 else 725 static if (isStringMap!V) 726 { 727 auto state = serializer.beginStruct(value); 728 auto keys = value.keys; 729 foreach (i, ref v; value.values) 730 { 731 serializer.putKey(keys[i]); 732 .serializeValue(serializer, v); 733 } 734 serializer.structEnd(state); 735 } 736 else 737 static if (serdeGetAnnotationMembersOut!V.length || isAnnotated!V) 738 { 739 auto wrapperState = serializer.annotationWrapperBegin; 740 return serializeAnnotatedValue(serializer, value, wrapperState); 741 } 742 else 743 static if ((isIterable!V || isRefIterable!V) && 744 (!hasProxy!V || hasLikeList!V) && 745 !hasFields!V && 746 !isStdNullable!V) 747 { 748 static if(is(V : E[], E) && !is(V : D[N], D, size_t N)) 749 { 750 if (value is null) 751 { 752 serializer.putNull(nullTypeCodeOf!V); 753 return; 754 } 755 } 756 static if (isSomeChar!(ForeachType!V)) 757 { 758 import mir.format: stringBuf; 759 auto buf = stringBuf; 760 static if (isIterable!(const V)) 761 { 762 static if (is(typeof(()@safe{foreach (elem; value){}}))) 763 foreach (elem; value) buf.put(elem); 764 else () @trusted 765 { 766 pragma(msg, "Mir warning: can't @safely iterate ", (const V).stringof); 767 foreach (elem; value) buf.put(elem); 768 }(); 769 } 770 else 771 { 772 pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof); 773 static if (!is(typeof(()@safe{foreach (elem; (()@trusted => cast() value)()){}}))) 774 pragma(msg, "Mir warning: can't @safely iterate ", (V).stringof); 775 () @trusted 776 { 777 foreach (elem; cast() value) buf.put(elem); 778 }(); 779 } 780 serializer.putValue(buf.data); 781 } 782 else 783 { 784 auto state = serializer.beginList(value); 785 static if (isRefIterable!V) 786 { 787 static if (__traits(hasMember, V, "lightScope")) 788 { 789 foreach (ref elem; value.lightScope) 790 { 791 serializer.elemBegin; 792 serializer.serializeValue(elem); 793 } 794 } 795 else 796 static if (isRefIterable!(const V)) 797 { 798 static if (is(typeof(()@safe{foreach (ref elem; value){}}))) 799 foreach (ref elem; value) 800 { 801 serializer.elemBegin; 802 serializer.serializeValue(elem); 803 } 804 else ()@trusted 805 { 806 pragma(msg, "Mir warning: can't @safely iterate ", (const V).stringof); 807 foreach (ref elem; value) 808 { 809 serializer.elemBegin; 810 serializer.serializeValue(elem); 811 } 812 } (); 813 } 814 else 815 { 816 pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof); 817 foreach (ref elem; (()@trusted => cast() value)()) 818 { 819 serializer.elemBegin; 820 serializer.serializeValue(elem); 821 } 822 } 823 } 824 else 825 { 826 static if (__traits(hasMember, V, "lightScope")) 827 { 828 foreach (elem; value.lightScope) 829 { 830 serializer.elemBegin; 831 serializer.serializeValue(elem); 832 } 833 } 834 else 835 static if (isIterable!(const V)) 836 { 837 foreach (elem; value) 838 { 839 serializer.elemBegin; 840 serializer.serializeValue(elem); 841 } 842 } 843 else 844 { 845 pragma(msg, "Mir warning: removing const qualifier to iterate. Implement `auto opIndex() @safe scope const` for ", (V).stringof); 846 static if (is(typeof(()@safe{foreach (elem; (()@trusted => cast() value)()){}}))) 847 foreach (elem; (()@trusted => cast() value)()) 848 { 849 serializer.elemBegin; 850 serializer.serializeValue(elem); 851 } 852 else ()@trusted 853 { 854 pragma(msg, "Mir warning: can't @safely iterate ", (V).stringof); 855 foreach (elem; cast() value) 856 { 857 serializer.elemBegin; 858 serializer.serializeValue(elem); 859 } 860 } (); 861 } 862 } 863 serializer.listEnd(state); 864 } 865 } 866 else 867 static if(hasUDA!(V, serdeLikeList)) 868 { 869 static assert(0); 870 } 871 else 872 static if(hasUDA!(V, serdeLikeStruct)) 873 { 874 static if (is(V : E[T], E, T)) 875 { 876 if (value is null) 877 { 878 serializer.putNull(nullTypeCodeOf!V); 879 continue F; 880 } 881 } 882 auto valState = serializer.beginStruct(value); 883 884 import mir.algebraic: isVariant, visit; 885 static if (__traits(hasMember, value, "byKeyValue")) 886 { 887 static if (is(typeof(()@safe {foreach (keyElem; value.byKeyValue){}}))) 888 foreach (keyElem; value.byKeyValue) 889 { 890 static if (!isVariant!(typeof(keyElem.key))) 891 serializer.putKey(keyElem.key); 892 else 893 { 894 if (keyElem.key._is!string) 895 serializer.putKey(keyElem.key.trustedGet!string); 896 else 897 if (keyElem.key._is!long) 898 serializer.putKey(keyElem.key.trustedGet!long.to!string); 899 else 900 if (keyElem.key._is!double) 901 serializer.putKey(keyElem.key.trustedGet!double.to!string); 902 else 903 if (keyElem.key._is!Timestamp) 904 serializer.putKey(keyElem.key.trustedGet!Timestamp.to!string); 905 } 906 serializer.serializeValue(keyElem.value); 907 } 908 else 909 { 910 pragma(msg, "Mir warning: can't safely iterate ", typeof(value)); 911 () @trusted { 912 foreach (keyElem; (cast() value).byKeyValue) 913 { 914 static if (!isVariant!(typeof(keyElem.key))) 915 serializer.putKey(keyElem.key); 916 else 917 { 918 if (keyElem.key._is!string) 919 serializer.putKey(keyElem.key.trustedGet!string); 920 else 921 if (keyElem.key._is!long) 922 serializer.putKey(keyElem.key.trustedGet!long.to!string); 923 else 924 if (keyElem.key._is!double) 925 serializer.putKey(keyElem.key.trustedGet!double.to!string); 926 else 927 if (keyElem.key._is!Timestamp) 928 serializer.putKey(keyElem.key.trustedGet!Timestamp.to!string); 929 } 930 serializer.serializeValue(keyElem.value); 931 } 932 }();} 933 } 934 else 935 { 936 foreach (key, ref elem; value) 937 { 938 static if (!isVariant!(typeof(key))) 939 serializer.putKey(key); 940 else 941 { 942 if (key._is!string) 943 serializer.putKey(key.trustedGet!string); 944 else 945 if (key._is!long) 946 serializer.putKey(key.trustedGet!long.to!string); 947 else 948 if (key._is!double) 949 serializer.putKey(key.trustedGet!double.to!string); 950 else 951 if (key._is!Timestamp) 952 serializer.putKey(key.trustedGet!Timestamp.to!string); 953 } 954 serializer.serializeValue(elem); 955 } 956 } 957 serializer.structEnd(valState); 958 } 959 else 960 static if (isAlgebraicAliasThis!V) 961 { 962 serializeValue(serializer, __traits(getMember, value, __traits(getAliasThis, V))); 963 } 964 else 965 static if (is(V == Algebraic!TypeSet, TypeSet...) && (!isStdNullable!V || Algebraic!TypeSet.AllowedTypes.length != 2)) 966 { 967 import mir.algebraic: match; 968 match!( 969 serializeVoidHandlerWithSerializer, 970 staticMap!(serializeAlgebraicAnnotation!S, Filter!(serdeHasAlgebraicAnnotation, V.AllowedTypes)), 971 serializeValue, 972 )(serializer, value); 973 } 974 else 975 static if(staticIndexOf!("serialize", __traits(allMembers, V)) >= 0) 976 { 977 alias soverloads = getSerializeOverloads!(S, V); 978 static if (__traits(hasMember, soverloads, "best") || !__traits(hasMember, soverloads, "script")) 979 { 980 static if (__traits(compiles, value.serialize(serializer)) || !hasUDA!(V, serdeProxy)) 981 value.serialize(serializer); 982 else 983 serializeValue(serializer, to!(serdeGetProxy!V)(value)); 984 985 } 986 else 987 static if (__traits(hasMember, soverloads, "script")) 988 { 989 import mir.ser.interfaces: SerializerWrapper; 990 scope wserializer = new SerializerWrapper!S(serializer); 991 auto iserializer = wserializer.ISerializer; 992 value.serialize(iserializer); 993 } 994 return; 995 } 996 else 997 static if (hasUDA!(V, serdeProxy)) 998 { 999 serializeProxyCastImpl!(S, V)(serializer, value); 1000 return; 1001 } 1002 else 1003 static if (isStdNullable!V || isNullable!V) 1004 { 1005 if(value.isNull) 1006 { 1007 serializer.putNull(nullTypeCodeOf!(typeof(V.init.get()))); 1008 return; 1009 } 1010 return serializeValue(serializer, value.get); 1011 } 1012 else 1013 static if (is(typeof(Timestamp(V.init)))) 1014 { 1015 serializer.putValue(Timestamp(value)); 1016 return; 1017 } 1018 else 1019 { 1020 return serializeValueImpl(serializer, value); 1021 } 1022 } 1023 1024 private template getSerializeOverloads(S, alias value) 1025 { 1026 import mir.ser.interfaces: ISerializer; 1027 static foreach (i, so; __traits(getOverloads, value, "serialize")) 1028 { 1029 static if (!__traits(isTemplate, value.serialize)) 1030 { 1031 static if (is(Parameters!so[0] == S)) 1032 { 1033 enum best = i; 1034 } 1035 else 1036 { 1037 static if (is(Parameters!so[0] == ISerializer)) 1038 { 1039 enum script = i; 1040 } 1041 } 1042 } 1043 } 1044 } 1045 1046 private struct UntrustDummy 1047 { 1048 long[] a; 1049 double b; 1050 bool c; 1051 string[] d; 1052 } 1053 1054 import std.traits: isFunctionPointer, isDelegate; 1055 1056 auto assumePure(T)(T t) @trusted 1057 if (isFunctionPointer!T || isDelegate!T) 1058 { 1059 pragma(inline, false); 1060 enum attrs = functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 1061 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 1062 } 1063 1064 1065 /// Mir types 1066 version(mir_ion_test) 1067 unittest 1068 { 1069 import mir.bignum.integer; 1070 import mir.date; 1071 import mir.ser.json: serializeJson; 1072 import mir.ser.text: serializeText; 1073 assert(Date(2021, 4, 24).serializeJson == `"2021-04-24"`); 1074 assert(BigInt!2(123).serializeJson == `123`); 1075 assert(Date(2021, 4, 24).serializeText == `2021-04-24`); 1076 assert(BigInt!2(123).serializeText == `123`); 1077 } 1078 1079 /// Alias this support 1080 version(mir_ion_test) 1081 unittest 1082 { 1083 struct S 1084 { 1085 int u; 1086 } 1087 1088 struct C 1089 { 1090 int b; 1091 S s; 1092 alias s this; 1093 } 1094 1095 import mir.ser.json: serializeJson; 1096 assert(C(4, S(3)).serializeJson == `{"u":3,"b":4}`); 1097 } 1098 1099 /// Custom `serialize` 1100 version(mir_ion_test) 1101 unittest 1102 { 1103 struct S 1104 { 1105 void serialize(S)(scope ref S serializer) scope const @safe 1106 { 1107 auto state = serializer.structBegin(1); 1108 serializer.putKey("foo"); 1109 serializer.putValue("bar"); 1110 serializer.structEnd(state); 1111 } 1112 } 1113 1114 import mir.ser.json: serializeJson; 1115 assert(serializeJson(S()) == `{"foo":"bar"}`); 1116 } 1117 1118 /// Nullable type serialization 1119 version(mir_ion_test) 1120 unittest 1121 { 1122 import mir.ser.json: serializeJson; 1123 import mir.algebraic: Nullable; 1124 1125 struct Nested 1126 { 1127 float f; 1128 } 1129 1130 struct T 1131 { 1132 string str; 1133 Nullable!Nested nested; 1134 } 1135 1136 T t; 1137 assert(t.serializeJson == `{"str":"","nested":{}}`); 1138 t.str = "txt"; 1139 t.nested = Nested(123); 1140 assert(t.serializeJson == `{"str":"txt","nested":{"f":123.0}}`); 1141 } 1142 1143 /// 1144 version(mir_ion_test) 1145 unittest 1146 { 1147 import mir.algebraic; 1148 import mir.small_string; 1149 1150 @serdeAlgebraicAnnotation("$B") 1151 static struct B 1152 { 1153 double number; 1154 } 1155 1156 @serdeAlgebraicAnnotation("$A") 1157 static struct A 1158 { 1159 @serdeAnnotation 1160 SmallString!32 id1; 1161 1162 @serdeAnnotation 1163 string[] id2; 1164 1165 B c; 1166 alias c this; 1167 1168 string s; 1169 } 1170 1171 1172 static assert(serdeHasAlgebraicAnnotation!B); 1173 static assert(serdeGetAlgebraicAnnotation!B == "$B"); 1174 static assert(serdeHasAlgebraicAnnotation!A); 1175 static assert(serdeGetAlgebraicAnnotation!A == "$A"); 1176 1177 @serdeAlgebraicAnnotation("$c") 1178 static struct C 1179 { 1180 } 1181 1182 @serdeAlgebraicAnnotation("$E") 1183 enum E { e1, e2 } 1184 1185 @serdeAlgebraicAnnotation("$S") 1186 static struct S 1187 { 1188 @serdeAnnotation 1189 string sid; 1190 1191 @serdeAnnotation 1192 string sid2; 1193 1194 alias Data = Nullable!(A, C, long, E); 1195 1196 alias data this; 1197 1198 Data data; 1199 } 1200 1201 1202 @serdeAlgebraicAnnotation("$Y") 1203 static struct Y 1204 { 1205 alias Data = Nullable!(A, C, long); 1206 1207 alias data this; 1208 1209 Data data; 1210 } 1211 1212 1213 import mir.ion.conv: ion2text; 1214 import mir.ser.ion: serializeIon; 1215 import mir.ser.text: serializeText; 1216 import mir.test; 1217 1218 () { 1219 Nullable!S value = S("LIBOR", "S", S.Data(A("Rate".SmallString!32, ["USD", "GBP"]))); 1220 static immutable text = `LIBOR::S::$A::Rate::USD::GBP::{number:nan,s:null.string}`; 1221 value.serializeText.should == text; 1222 auto binary = value.serializeIon; 1223 binary.ion2text.should == text; 1224 import mir.deser.ion: deserializeIon; 1225 binary.deserializeIon!S.serializeText.should == text; 1226 } (); 1227 1228 () { 1229 S value = S("LIBOR", "S", S.Data(E.e2)); 1230 static immutable text = `LIBOR::S::$E::e2`; 1231 value.serializeText.should == text; 1232 auto binary = value.serializeIon; 1233 binary.ion2text.should == text; 1234 import mir.deser.ion: deserializeIon; 1235 binary.deserializeIon!S.serializeText.should == text; 1236 } (); 1237 1238 () { 1239 auto value = Y(Y.Data(A("Rate".SmallString!32, ["USD", "GBP"]))); 1240 static immutable text = `$A::Rate::USD::GBP::{number:nan,s:null.string}`; 1241 auto binary = value.serializeIon; 1242 binary.ion2text.should == text; 1243 import mir.deser.ion: deserializeIon; 1244 binary.deserializeIon!Y.serializeText.should == text; 1245 } (); 1246 1247 () { 1248 auto value = S("USD", "S", S.Data(3)); 1249 static immutable text = `USD::S::3`; 1250 auto binary = value.serializeIon; 1251 binary.ion2text.should == text; 1252 import mir.deser.ion: deserializeIon; 1253 binary.deserializeIon!S.serializeText.should == text; 1254 } (); 1255 } 1256 1257 /++ 1258 +/ 1259 auto beginList(S, V)(scope ref S serializer, scope ref V value) 1260 { 1261 static if (__traits(compiles, serializer.listBegin)) 1262 { 1263 return serializer.listBegin; 1264 } 1265 else 1266 { 1267 import mir.primitives: walkLength; 1268 return serializer.listBegin(value.walkLength); 1269 } 1270 } 1271 1272 /++ 1273 +/ 1274 auto beginSexp(S, V)(scope ref S serializer, scope ref V value) 1275 { 1276 static if (__traits(compiles, serializer.sexpBegin)) 1277 { 1278 return serializer.sexpBegin; 1279 } 1280 else 1281 { 1282 import mir.primitives: walkLength; 1283 return serializer.sexpBegin(value.walkLength); 1284 } 1285 } 1286 1287 /++ 1288 +/ 1289 auto beginStruct(S, V)(scope ref S serializer, scope ref V value) 1290 { 1291 static if (__traits(compiles, serializer.structBegin)) 1292 { 1293 return serializer.structBegin; 1294 } 1295 else 1296 { 1297 import mir.primitives: walkLength; 1298 return serializer.structBegin(value.walkLength); 1299 } 1300 } 1301 1302 version (Have_mir_bloomberg) 1303 { 1304 import mir.ser.bloomberg : BloombergElement; 1305 /// 1306 void serializeValue(S)(scope ref S serializer, scope const(BloombergElement)* value) 1307 { 1308 import mir.ser.bloomberg : impl = serializeValue; 1309 return impl(serializer, value); 1310 } 1311 }