1 /++ 2 This module contains the [meldInto] functions; functions that take two 3 structs or classes of the same type and combine them, creating a resulting 4 object with the union of the members of both parents. Array and associative 5 array variants exist too. 6 7 Example: 8 --- 9 struct Foo 10 { 11 string abc; 12 string def; 13 int i; 14 float f; 15 double d; 16 } 17 18 Foo f1; // = new Foo; 19 f1.abc = "ABC"; 20 f1.def = "DEF"; 21 22 Foo f2; // = new Foo; 23 f2.abc = "this won't get copied"; 24 f2.def = "neither will this"; 25 f2.i = 42; 26 f2.f = 3.14f; 27 28 f2.meldInto(f1); 29 30 with (f1) 31 { 32 import std.math : isNaN; 33 34 assert(abc == "ABC"); 35 assert(def == "DEF"); 36 assert(i == 42); 37 assert(f == 3.14f); 38 assert(d.isNaN); 39 } 40 --- 41 +/ 42 module lu.meld; 43 44 private: 45 46 import lu.traits : isMerelyArray; 47 import std.traits : isAggregateType, isArray, isAssociativeArray, isMutable; 48 49 public: 50 51 52 /++ 53 To what extent a source should overwrite a target when melding. 54 +/ 55 enum MeldingStrategy 56 { 57 /++ 58 Takes care not to overwrite settings when either the source or the 59 target is `.init`. 60 +/ 61 conservative, 62 63 /++ 64 Only considers the `init`-ness of the source, so as not to overwrite 65 things with empty strings, but otherwise always considers the source to 66 trump the target. 67 +/ 68 aggressive, 69 70 /++ 71 Works like aggressive but also always overwrites bools, regardless of 72 falseness. 73 +/ 74 overwriting, 75 } 76 77 78 /++ 79 UDA conveying that this member's value cannot or should not be melded. 80 +/ 81 enum Unmeldable; 82 83 84 // meldInto 85 /++ 86 Takes two structs or classes of the same type and melds them together, 87 making the members a union of the two. 88 89 In the case of classes it only overwrites members in `intoThis` that are 90 `typeof(member).init`, so only unset members get their values overwritten by 91 the melding class. It also does not work with static members. 92 93 In the case of structs it also overwrites members that still have their 94 default values, in cases where such is applicable. 95 96 Supply a template parameter [MeldingStrategy] to decide to which extent 97 values are overwritten. 98 99 Example: 100 --- 101 struct Foo 102 { 103 string abc; 104 int def; 105 bool b = true; 106 } 107 108 Foo foo, bar; 109 foo.abc = "from foo" 110 foo.b = false; 111 bar.def = 42; 112 foo.meldInto(bar); 113 114 assert(bar.abc == "from foo"); 115 assert(bar.def == 42); 116 assert(!bar.b); // false overwrote default value true 117 --- 118 119 Params: 120 strategy = To what extent the source object should overwrite set 121 (non-`init`) values in the receiving object. 122 meldThis = Object to meld (source). 123 intoThis = Reference to object to meld (target). 124 +/ 125 void meldInto(MeldingStrategy strategy = MeldingStrategy.conservative, QualThing, Thing) 126 (auto ref QualThing meldThis, ref Thing intoThis) 127 if (isAggregateType!Thing && is(QualThing : Thing) && isMutable!Thing) 128 { 129 import std.traits : hasUDA, isAggregateType, isArray, isAssignable, isPointer, 130 isSomeString, isType, hasUnsharedAliasing; 131 132 static if (is(Thing == struct) && (strategy == MeldingStrategy.conservative)) 133 { 134 if (meldThis == Thing.init) 135 { 136 // We're merging an .init with something, and .init does not have 137 // any special default values. Nothing would get melded, so exit early. 138 return; 139 } 140 141 static if (!hasUnsharedAliasing!Thing) 142 { 143 if (intoThis == Thing.init) 144 { 145 // Likewise we're merging into an .init, so just fast-path overwrite. 146 intoThis = meldThis; 147 return; 148 } 149 } 150 } 151 152 foreach (immutable i, ref _; intoThis.tupleof) 153 { 154 static if (!isType!(intoThis.tupleof[i])) 155 { 156 alias T = typeof(intoThis.tupleof[i]); 157 158 static if (hasUDA!(intoThis.tupleof[i], Unmeldable)) 159 { 160 // Do nothing 161 } 162 else static if (isAggregateType!T) 163 { 164 // Recurse 165 meldThis.tupleof[i].meldInto!strategy(intoThis.tupleof[i]); 166 } 167 else static if (isAssignable!T) 168 { 169 // Overwriting strategy overwrites everything except where the 170 // source is clearly `.init`. 171 // Aggressive strategy works like overwriting except it doesn't 172 // blindly overwrite struct bools. 173 static if ((strategy == MeldingStrategy.overwriting) || 174 (strategy == MeldingStrategy.aggressive)) 175 { 176 static if (is(T == float) || is(T == double)) 177 { 178 import std.math : isNaN; 179 180 if (!meldThis.tupleof[i].isNaN) 181 { 182 intoThis.tupleof[i] = meldThis.tupleof[i]; 183 } 184 } 185 else static if (is(T == bool)) 186 { 187 static if (strategy == MeldingStrategy.overwriting) 188 { 189 // Non-discriminately overwrite bools 190 intoThis.tupleof[i] = meldThis.tupleof[i]; 191 } 192 else static if (strategy == MeldingStrategy.aggressive) 193 { 194 static if (is(Thing == class)) 195 { 196 // We cannot tell whether or not it has the same value as 197 // `Thing.init` does, as it would need to be instantiated. 198 // Assume overwrite? 199 intoThis.tupleof[i] = meldThis.tupleof[i]; 200 } 201 else 202 { 203 if (intoThis.tupleof[i] == Thing.init.tupleof[i]) 204 { 205 intoThis.tupleof[i] = meldThis.tupleof[i]; 206 } 207 } 208 } 209 else 210 { 211 static assert(0, "Logic error; unexpected `MeldingStrategy` " ~ 212 "passed to struct/class `meldInto`"); 213 } 214 } 215 else static if (isArray!T && !isSomeString!T) 216 { 217 // Pass on to array melder 218 meldThis.tupleof[i].meldInto!strategy(intoThis.tupleof[i]); 219 } 220 else static if (isAssociativeArray!T) 221 { 222 // Pass on to AA melder 223 meldThis.tupleof[i].meldInto!strategy(intoThis.tupleof[i]); 224 } 225 else static if (isPointer!T) 226 { 227 // Aggressive and/or overwriting, so just overwrite the pointer? 228 intoThis.tupleof[i] = meldThis.tupleof[i]; 229 } 230 else static if (is(Thing == class)) 231 { 232 // Can't compare with Thing.init.tupleof[i] 233 intoThis.tupleof[i] = meldThis.tupleof[i]; 234 } 235 else 236 { 237 if (meldThis.tupleof[i] != Thing.init.tupleof[i]) 238 { 239 intoThis.tupleof[i] = meldThis.tupleof[i]; 240 } 241 } 242 } 243 // Conservative strategy takes care not to overwrite members 244 // with non-`init` values. 245 else static if (strategy == MeldingStrategy.conservative) 246 { 247 static if (is(T == float) || is(T == double)) 248 { 249 import std.math : isNaN; 250 251 if (intoThis.tupleof[i].isNaN) 252 { 253 intoThis.tupleof[i] = meldThis.tupleof[i]; 254 } 255 } 256 else static if (is(T == enum)) 257 { 258 if (meldThis.tupleof[i] > intoThis.tupleof[i]) 259 { 260 intoThis.tupleof[i] = meldThis.tupleof[i]; 261 } 262 } 263 else static if (is(T == string[])) 264 { 265 import std.algorithm.searching : canFind; 266 267 if (!intoThis.tupleof[i].canFind(meldThis.tupleof[i])) 268 { 269 intoThis.tupleof[i] ~= meldThis.tupleof[i]; 270 } 271 } 272 else static if (isArray!T && !isSomeString!T) 273 { 274 // Pass on to array melder 275 meldThis.tupleof[i].meldInto!strategy(intoThis.tupleof[i]); 276 } 277 else static if (isAssociativeArray!T) 278 { 279 // Pass on to AA melder 280 meldThis.tupleof[i].meldInto!strategy(intoThis.tupleof[i]); 281 } 282 else static if (isPointer!T) 283 { 284 // Conservative, so check if null and overwrite if so 285 if (!intoThis.tupleof[i]) intoThis.tupleof[i] = meldThis.tupleof[i]; 286 } 287 else static if (is(T == bool)) 288 { 289 static if (is(Thing == class)) 290 { 291 // We cannot tell whether or not it has the same value as 292 // `Thing.init` does, as it would need to be instantiated. 293 // Assume overwrite? 294 intoThis.tupleof[i] = meldThis.tupleof[i]; 295 } 296 else 297 { 298 if (intoThis.tupleof[i] == Thing.init.tupleof[i]) 299 { 300 intoThis.tupleof[i] = meldThis.tupleof[i]; 301 } 302 } 303 } 304 else 305 { 306 /+ This is tricksy for bools. A value of false could be 307 false, or merely unset. If we're not overwriting, 308 let whichever side is true win out? +/ 309 310 static if (is(Thing == class)) 311 { 312 if (intoThis.tupleof[i] == T.init) 313 { 314 intoThis.tupleof[i] = meldThis.tupleof[i]; 315 } 316 } 317 else 318 { 319 if ((intoThis.tupleof[i] == T.init) || 320 (intoThis.tupleof[i] == Thing.init.tupleof[i])) 321 { 322 intoThis.tupleof[i] = meldThis.tupleof[i]; 323 } 324 } 325 } 326 } 327 } 328 else 329 { 330 /*import std.traits : Unqual; 331 static assert(0, Unqual!T.stringof ~ " `" ~ 332 Unqual!QualThing.stringof ~ '.' ~ 333 __traits(identifier, meldThis.tupleof[i]) ~ "` is not meldable!");*/ 334 } 335 } 336 } 337 } 338 339 /// 340 unittest 341 { 342 import std.conv : to; 343 344 static struct TestFoo 345 { 346 string abc; 347 string def; 348 int i; 349 float f; 350 double d; 351 int[string] aa; 352 int[] arr; 353 int* ip; 354 355 void blah() {} 356 357 const string kek; 358 immutable bool bur; 359 360 this(bool bur) 361 { 362 kek = "uden lo"; 363 this.bur = bur; 364 } 365 } 366 367 TestFoo f1; // = new TestFoo; 368 f1.abc = "ABC"; 369 f1.def = "DEF"; 370 f1.aa = [ "abc" : 123, "ghi" : 789 ]; 371 f1.arr = [ 1, 0, 3, 0, 5 ]; 372 373 TestFoo f2; // = new TestFoo; 374 f2.abc = "this won't get copied"; 375 f2.def = "neither will this"; 376 f2.i = 42; 377 f2.f = 3.14f; 378 f2.aa = [ "abc" : 999, "def" : 456 ]; 379 f2.arr = [ 0, 2, 0, 4 ]; 380 381 f2.meldInto(f1); 382 383 with (f1) 384 { 385 import std.math : isNaN; 386 387 assert((abc == "ABC"), abc); 388 assert((def == "DEF"), def); 389 assert((i == 42), i.to!string); 390 assert((f == 3.14f), f.to!string); 391 assert(d.isNaN, d.to!string); 392 assert((aa == [ "abc" : 123, "def" : 456, "ghi" : 789 ]), aa.to!string); 393 assert((arr == [ 1, 2, 3, 4, 5 ]), arr.to!string); 394 } 395 396 TestFoo f3; // new TestFoo; 397 f3.abc = "abc"; 398 f3.def = "def"; 399 f3.i = 100_135; 400 f3.f = 99.9f; 401 f3.aa = [ "abc" : 123, "ghi" : 789 ]; 402 f3.arr = [ 1, 0, 3, 0, 5 ]; 403 404 TestFoo f4; // new TestFoo; 405 f4.abc = "OVERWRITTEN"; 406 f4.def = "OVERWRITTEN TOO"; 407 f4.i = 0; 408 f4.f = 0.1f; 409 f4.d = 99.999; 410 f4.aa = [ "abc" : 999, "def" : 456 ]; 411 f4.arr = [ 9, 2, 0, 4 ]; 412 413 f4.meldInto!(MeldingStrategy.aggressive)(f3); 414 415 with (f3) 416 { 417 static if (__VERSION__ >= 2091) 418 { 419 import std.math : isClose; 420 } 421 else 422 { 423 import std.math : approxEqual; 424 alias isClose = approxEqual; 425 } 426 427 assert((abc == "OVERWRITTEN"), abc); 428 assert((def == "OVERWRITTEN TOO"), def); 429 assert((i == 100_135), i.to!string); // 0 is int.init 430 assert((f == 0.1f), f.to!string); 431 assert(isClose(d, 99.999), d.to!string); 432 assert((aa == [ "abc" : 999, "def" : 456, "ghi" : 789 ]), aa.to!string); 433 assert((arr == [ 9, 2, 3, 4, 5 ]), arr.to!string); 434 } 435 436 // Overwriting is just aggressive but always overwrites bools. 437 438 struct User 439 { 440 enum Class { anyone, blacklist, whitelist, admin } 441 string nickname; 442 string alias_; 443 string ident; 444 string address; 445 string login; 446 bool special; 447 Class class_; 448 } 449 450 User one; 451 with (one) 452 { 453 nickname = "foobar"; 454 ident = "NaN"; 455 address = "herpderp.net"; 456 special = false; 457 class_ = User.Class.whitelist; 458 } 459 460 User two; 461 with (two) 462 { 463 nickname = "foobar^"; 464 alias_ = "FooBar"; 465 address = "asdf.org"; 466 login = "kamelusu"; 467 special = true; 468 class_ = User.Class.blacklist; 469 } 470 471 //import lu.conv : Enum; 472 473 User twoCopy = two; 474 475 one.meldInto!(MeldingStrategy.conservative)(two); 476 with (two) 477 { 478 assert((nickname == "foobar^"), nickname); 479 assert((alias_ == "FooBar"), alias_); 480 assert((ident == "NaN"), ident); 481 assert((address == "asdf.org"), address); 482 assert((login == "kamelusu"), login); 483 assert(special); 484 assert(class_ == User.Class.whitelist);//, Enum!(User.Class).toString(class_)); 485 } 486 487 one.class_ = User.Class.blacklist; 488 489 one.meldInto!(MeldingStrategy.overwriting)(twoCopy); 490 with (twoCopy) 491 { 492 assert((nickname == "foobar"), nickname); 493 assert((alias_ == "FooBar"), alias_); 494 assert((ident == "NaN"), ident); 495 assert((address == "herpderp.net"), address); 496 assert((login == "kamelusu"), login); 497 assert(!special); 498 assert(class_ == User.Class.blacklist);//, Enum!(User.Class).toString(class_)); 499 } 500 501 struct EnumThing 502 { 503 enum Enum { unset, one, two, three } 504 Enum enum_; 505 } 506 507 EnumThing e1; 508 EnumThing e2; 509 e2.enum_ = EnumThing.Enum.three; 510 assert(e1.enum_ == EnumThing.Enum.init);//, Enum!(EnumThing.Enum).toString(e1.enum_)); 511 e2.meldInto(e1); 512 assert(e1.enum_ == EnumThing.Enum.three);//, Enum!(EnumThing.Enum).toString(e1.enum_)); 513 514 struct WithArray 515 { 516 string[] arr; 517 } 518 519 WithArray w1, w2; 520 w1.arr = [ "arr", "matey", "I'ma" ]; 521 w2.arr = [ "pirate", "stereotype", "unittest" ]; 522 w2.meldInto(w1); 523 assert((w1.arr == [ "arr", "matey", "I'ma", "pirate", "stereotype", "unittest" ]), w1.arr.to!string); 524 525 WithArray w3, w4; 526 w3.arr = [ "arr", "matey", "I'ma" ]; 527 w4.arr = [ "arr", "matey", "I'ma" ]; 528 w4.meldInto(w3); 529 assert((w3.arr == [ "arr", "matey", "I'ma" ]), w3.arr.to!string); 530 531 struct Server 532 { 533 string address; 534 } 535 536 struct Bot 537 { 538 string nickname; 539 Server server; 540 } 541 542 Bot b1, b2; 543 b1.nickname = "foobar"; 544 b1.server.address = "freenode.net"; 545 546 assert(!b2.nickname.length, b2.nickname); 547 assert(!b2.server.address.length, b2.nickname); 548 b1.meldInto(b2); 549 assert((b2.nickname == "foobar"), b2.nickname); 550 assert((b2.server.address == "freenode.net"), b2.server.address); 551 552 b2.nickname = "harbl"; 553 b2.server.address = "rizon.net"; 554 555 b2.meldInto!(MeldingStrategy.aggressive)(b1); 556 assert((b1.nickname == "harbl"), b1.nickname); 557 assert((b1.server.address == "rizon.net"), b1.server.address); 558 559 class Class 560 { 561 static int i; 562 string s; 563 bool b; 564 } 565 566 Class abc = new Class; 567 abc.i = 42; 568 abc.s = "some string"; 569 abc.b = true; 570 571 Class def = new Class; 572 def.s = "other string"; 573 abc.meldInto(def); 574 575 assert((def.i == 42), def.i.to!string); 576 assert((def.s == "other string"), def.s); 577 assert(def.b); 578 579 abc.meldInto!(MeldingStrategy.aggressive)(def); 580 assert((def.s == "some string"), def.s); 581 582 struct Bools 583 { 584 bool a = true; 585 bool b = false; 586 } 587 588 Bools bools1, bools2, inverted, backupInverted; 589 590 bools2.a = false; 591 592 inverted.a = false; 593 inverted.b = true; 594 backupInverted = inverted; 595 596 bools2.meldInto(bools1); 597 assert(!bools1.a); 598 assert(!bools1.b); 599 600 bools2.meldInto(inverted); 601 assert(!inverted.a); 602 assert(inverted.b); 603 inverted = backupInverted; 604 605 bools2.meldInto!(MeldingStrategy.overwriting)(inverted); 606 assert(!inverted.a); 607 assert(!inverted.b); 608 inverted = backupInverted; 609 610 struct Asdf 611 { 612 string nickname = "sadf"; 613 string server = "asdf.net"; 614 } 615 616 Asdf a, b; 617 a.server = "a"; 618 b.server = "b"; 619 b.meldInto!(MeldingStrategy.aggressive)(a); 620 assert((a.server == "b"), a.server); 621 622 a.server = "a"; 623 b.server = Asdf.init.server; 624 b.meldInto!(MeldingStrategy.aggressive)(a); 625 assert((a.server == "a"), a.server); 626 627 struct Blah 628 { 629 int yes = 42; 630 @Unmeldable int no = 24; 631 } 632 633 Blah blah1, blah2; 634 blah1.yes = 5; 635 blah1.no = 42; 636 blah1.meldInto!(MeldingStrategy.aggressive)(blah2); 637 assert((blah2.yes == 5), blah2.yes.to!string); 638 assert((blah2.no == 24), blah2.no.to!string); 639 } 640 641 642 // meldInto (array) 643 /++ 644 Takes two arrays and melds them together, making a union of the two. 645 646 It only overwrites members that are `T.init`, so only unset 647 fields get their values overwritten by the melding array. Supply a 648 template parameter [MeldingStrategy.aggressive] to make it overwrite if the 649 melding array's field is not `T.init`. Furthermore use 650 [MeldingStrategy.overwriting] if working with bool members. 651 652 Example: 653 --- 654 int[] arr1 = [ 1, 2, 3, 0, 0, 0 ]; 655 int[] arr2 = [ 0, 0, 0, 4, 5, 6 ]; 656 arr1.meldInto!(MeldingStrategy.conservative)(arr2); 657 658 assert(arr2 == [ 1, 2, 3, 4, 5, 6 ]); 659 --- 660 661 Params: 662 strategy = To what extent the source object should overwrite set 663 (non-`init`) values in the receiving object. 664 meldThis = Array to meld (source). 665 intoThis = Reference to the array to meld (target). 666 +/ 667 void meldInto(MeldingStrategy strategy = MeldingStrategy.conservative, Array1, Array2) 668 (auto ref Array1 meldThis, ref Array2 intoThis) pure nothrow 669 if (isMerelyArray!Array1 && isMerelyArray!Array2 && isMutable!Array2) 670 { 671 import std.traits : isDynamicArray, isStaticArray; 672 673 static if (isDynamicArray!Array2) 674 { 675 if (!meldThis.length) 676 { 677 // Source empty, just return 678 return; 679 } 680 else if (!intoThis.length) 681 { 682 // Source has content but target empty, just inherit 683 intoThis = meldThis.dup; 684 return; 685 } 686 687 // Ensure there's room for all elements 688 if (meldThis.length > intoThis.length) intoThis.length = meldThis.length; 689 } 690 else static if (isStaticArray!Array1 && isStaticArray!Array2) 691 { 692 static if (Array1.length == Array2.length) 693 { 694 if (meldThis == Array1.init) 695 { 696 // Source empty, just return 697 return; 698 } 699 else if (intoThis == Array2.init) 700 { 701 // Source has content but target empty, just inherit 702 intoThis = meldThis; // value type, no need for .dup 703 return; 704 } 705 } 706 else 707 { 708 import std.format : format; 709 static assert((Array2.length >= Array1.length), 710 "Cannot meld a larger `%s` static array into a smaller `%s` static one" 711 .format(Array1.stringof, Array2.stringof)); 712 } 713 } 714 else static if (isDynamicArray!Array1 && isStaticArray!Array2) 715 { 716 assert((meldThis.length <= Array2.length), 717 "Cannot meld a larger dynamic array into a smaller static one"); 718 } 719 else 720 { 721 import std.format : format; 722 static assert(0, "Attempted to meld an unsupported array type: `%s` into `%s`" 723 .format(Array1.stringof, Array2.stringof)); 724 } 725 726 foreach (immutable i, const val; meldThis) 727 { 728 static if (strategy == MeldingStrategy.conservative) 729 { 730 if ((val != typeof(val).init) && (intoThis[i] == typeof(intoThis[i]).init)) 731 { 732 intoThis[i] = val; 733 } 734 } 735 else static if (strategy == MeldingStrategy.aggressive) 736 { 737 if (val != typeof(val).init) 738 { 739 intoThis[i] = val; 740 } 741 } 742 else static if (strategy == MeldingStrategy.overwriting) 743 { 744 intoThis[i] = val; 745 } 746 else 747 { 748 static assert(0, "Logic error; unexpected `MeldingStrategy` passed to array `meldInto`"); 749 } 750 } 751 } 752 753 /// 754 unittest 755 { 756 import std.conv : to; 757 758 auto arr1 = [ 123, 0, 789, 0, 456, 0 ]; 759 auto arr2 = [ 0, 456, 0, 123, 0, 789 ]; 760 arr1.meldInto!(MeldingStrategy.conservative)(arr2); 761 assert((arr2 == [ 123, 456, 789, 123, 456, 789 ]), arr2.to!string); 762 763 auto yarr1 = [ 'Z', char.init, 'Z', char.init, 'Z' ]; 764 auto yarr2 = [ 'A', 'B', 'C', 'D', 'E', 'F' ]; 765 yarr1.meldInto!(MeldingStrategy.aggressive)(yarr2); 766 assert((yarr2 == [ 'Z', 'B', 'Z', 'D', 'Z', 'F' ]), yarr2.to!string); 767 768 auto harr1 = [ char.init, 'X' ]; 769 yarr1.meldInto(harr1); 770 assert((harr1 == [ 'Z', 'X', 'Z', char.init, 'Z' ]), harr1.to!string); 771 772 char[5] harr2 = [ '1', '2', '3', '4', '5' ]; 773 char[] harr3; 774 harr2.meldInto(harr3); 775 assert((harr2 == harr3), harr3.to!string); 776 777 int[3] asdf; 778 int[3] hasdf; 779 asdf.meldInto(hasdf); 780 781 int[] dyn = new int[2]; 782 int[3] stat; 783 dyn.meldInto(stat); 784 } 785 786 787 // meldInto 788 /++ 789 Takes two associative arrays and melds them together, making a union of the two. 790 791 This is largely the same as the array-version [meldInto] but doesn't need 792 the extensive template constraints it employs, so it might as well be kept separate. 793 794 Example: 795 --- 796 int[string] aa1 = [ "abc" : 42, "def" : -1 ]; 797 int[string] aa2 = [ "ghi" : 10, "jkl" : 7 ]; 798 arr1.meldInto(arr2); 799 800 assert("abc" in aa2); 801 assert("def" in aa2); 802 assert("ghi" in aa2); 803 assert("jkl" in aa2); 804 --- 805 806 Params: 807 strategy = To what extent the source object should overwrite set 808 (non-`init`) values in the receiving object. 809 meldThis = Associative array to meld (source). 810 intoThis = Reference to the associative array to meld (target). 811 +/ 812 void meldInto(MeldingStrategy strategy = MeldingStrategy.conservative, QualAA, AA) 813 (QualAA meldThis, ref AA intoThis) pure 814 if (isAssociativeArray!AA && is(QualAA : AA) && isMutable!AA) 815 { 816 if (!meldThis.length) 817 { 818 // Empty source 819 return; 820 } 821 else if (!intoThis.length) 822 { 823 // Empty target, just assign 824 intoThis = meldThis.dup; 825 return; 826 } 827 828 foreach (immutable key, val; meldThis) 829 { 830 static if (strategy == MeldingStrategy.conservative) 831 { 832 if (val == typeof(val).init) 833 { 834 // Source value is .init; do nothing 835 continue; 836 } 837 838 const target = key in intoThis; 839 840 if (!target || (*target == typeof(*target).init)) 841 { 842 // Target value doesn't exist or is .init; meld 843 intoThis[key] = val; 844 } 845 } 846 else static if ((strategy == MeldingStrategy.aggressive) || 847 (strategy == MeldingStrategy.overwriting)) 848 { 849 import std.traits : ValueType; 850 851 static if ((strategy == MeldingStrategy.overwriting) && 852 is(ValueType!AA == bool)) 853 { 854 // Always overwrite 855 intoThis[key] = val; 856 } 857 else 858 { 859 if (val != typeof(val).init) 860 { 861 // Target value doesn't exist; meld 862 intoThis[key] = val; 863 } 864 } 865 } 866 else 867 { 868 static assert(0, "Logic error; unexpected `MeldingStrategy` passed to AA `meldInto`"); 869 } 870 } 871 } 872 873 /// 874 unittest 875 { 876 bool[string] aa1; 877 bool[string] aa2; 878 879 aa1["a"] = true; 880 aa1["b"] = false; 881 aa2["c"] = true; 882 aa2["d"] = false; 883 884 assert("a" in aa1); 885 assert("b" in aa1); 886 assert("c" in aa2); 887 assert("d" in aa2); 888 889 aa1.meldInto!(MeldingStrategy.overwriting)(aa2); 890 891 assert("a" in aa2); 892 assert("b" in aa2); 893 894 string[string] saa1; 895 string[string] saa2; 896 897 saa1["a"] = "a"; 898 saa1["b"] = "b"; 899 saa2["c"] = "c"; 900 saa2["d"] = "d"; 901 902 saa1.meldInto!(MeldingStrategy.conservative)(saa2); 903 assert("a" in saa2); 904 assert("b" in saa2); 905 906 saa1["a"] = "A"; 907 saa1.meldInto!(MeldingStrategy.aggressive)(saa2); 908 assert(saa2["a"] == "A"); 909 }