1 module fuzzycopy; 2 3 import std.algorithm.iteration : map; 4 import std.array : array, empty; 5 import std.conv : to; 6 import std.format : format; 7 import std.range : ElementEncodingType; 8 import std.traits : isFunction, hasMember, isImplicitlyConvertible, 9 isIntegral, isFloatingPoint, isArray, isSomeString, FieldNameTuple; 10 import std.typecons : Nullable, nullable; 11 import std.stdio; 12 13 template isNullable(T) { 14 enum isNullable = is(T : Nullable!F, F); 15 } 16 17 template Type(T) { 18 static if(is(T : Nullable!F, F)) { 19 alias Type = F; 20 } else { 21 alias Type = T; 22 } 23 } 24 25 template extractBaseType(T) { 26 static if(is(T : Nullable!F, F)) { 27 alias extractBaseType = .extractBaseType!F; 28 } else static if(isArray!T && !isSomeString!T) { 29 alias extractBaseType = .extractBaseType!(ElementEncodingType!T); 30 } else { 31 alias extractBaseType = T; 32 } 33 } 34 35 unittest { 36 static assert(is(extractBaseType!float == float)); 37 static assert(is(extractBaseType!(Nullable!float) == float)); 38 static assert(is(extractBaseType!(Nullable!(float[])) == float)); 39 static assert(is(extractBaseType!(Nullable!(Nullable!float[])) == float)); 40 static assert(is(extractBaseType!string == string)); 41 static assert(is(extractBaseType!(Nullable!string) == string)); 42 static assert(is(extractBaseType!(Nullable!(string[])) == string)); 43 static assert(is(extractBaseType!(Nullable!(Nullable!string[])) == string)); 44 } 45 46 template arrayDepth(T) { 47 static if(isArray!T && !isSomeString!T) { 48 enum arrayDepth = 1 + .arrayDepth!(ElementEncodingType!T); 49 } else static if(is(T : Nullable!F, F)) { 50 enum arrayDepth = .arrayDepth!F; 51 } else { 52 enum arrayDepth = 0; 53 } 54 } 55 56 unittest { 57 static assert(arrayDepth!(int[]) == 1); 58 static assert(arrayDepth!(int) == 0); 59 static assert(arrayDepth!(int[][]) == 2); 60 static assert(arrayDepth!(Nullable!(int[])[]) == 2); 61 } 62 63 template StripNullable(T) { 64 static if(is(T : Nullable!F, F)) { 65 alias StripNullable = .StripNullable!F; 66 } else static if(isArray!T && !isSomeString!T) { 67 alias StripNullable = .StripNullable!(ElementEncodingType!T)[]; 68 } else { 69 alias StripNullable = T; 70 } 71 } 72 73 unittest { 74 static assert(is(StripNullable!(int) == int)); 75 static assert(is(StripNullable!(int[]) == int[])); 76 static assert(is(StripNullable!(Nullable!int[]) == int[])); 77 static assert(is(StripNullable!(Nullable!(int[])) == int[])); 78 static assert(is(StripNullable!(Nullable!(Nullable!int[])) == int[])); 79 80 struct Foo { 81 } 82 static assert(is(StripNullable!(Foo) == Foo)); 83 static assert(is(StripNullable!(Foo[]) == Foo[])); 84 static assert(is(StripNullable!(Nullable!Foo[]) == Foo[])); 85 static assert(is(StripNullable!(Nullable!(Foo[])) == Foo[])); 86 static assert(is(StripNullable!(Nullable!(Nullable!Foo[])) == Foo[])); 87 } 88 89 template StripTopNullable(T) { 90 static if(is(T : Nullable!F, F)) { 91 alias StripTopNullable = F; 92 } else { 93 alias StripTopNullable = T; 94 } 95 } 96 97 unittest { 98 static assert(is(StripTopNullable!(int) == int)); 99 static assert(is(StripTopNullable!(int[]) == int[])); 100 static assert(is(StripTopNullable!(Nullable!(int[])) == int[])); 101 static assert(is(StripTopNullable!(Nullable!(int[])) == int[])); 102 static assert(is(StripTopNullable!(Nullable!(Nullable!int[])) == Nullable!(int)[])); 103 } 104 105 template canBeConvertedWithTo(F,T) { 106 alias FT = extractBaseType!F; 107 alias TT = extractBaseType!T; 108 enum FD = arrayDepth!F; 109 enum TD = arrayDepth!T; 110 111 static if(isIntegral!FT && isIntegral!TT && FD == TD && FD == 0) { 112 enum canBeConvertedWithTo = true; 113 } else static if(isIntegral!FT && isFloatingPoint!TT 114 && FD == TD && FD == 0) 115 { 116 enum canBeConvertedWithTo = true; 117 } else static if(isFloatingPoint!FT && isIntegral!TT && FD == TD 118 && FD == 0) 119 { 120 enum canBeConvertedWithTo = true; 121 } else static if(isSomeString!FT && isSomeString!TT && FD == TD 122 && FD == 0) 123 { 124 enum canBeConvertedWithTo = true; 125 } else { 126 enum canBeConvertedWithTo = false; 127 } 128 } 129 130 unittest { 131 static assert(canBeConvertedWithTo!(int,byte)); 132 static assert(canBeConvertedWithTo!(byte,float)); 133 static assert(!canBeConvertedWithTo!(string,float)); 134 static assert(!canBeConvertedWithTo!(int[],float)); 135 static assert(!canBeConvertedWithTo!(int[],float[])); 136 static assert(!canBeConvertedWithTo!(byte[],double[])); 137 static assert(!canBeConvertedWithTo!(double[],ubyte[])); 138 } 139 140 auto getValue(TnN,F,string mem)(ref F f) { 141 alias FMem = typeof(__traits(getMember, F, mem)); 142 enum FiN = isNullable!FMem; 143 144 struct Result { 145 Type!FMem value; 146 bool isNull; 147 } 148 149 Result ret; 150 151 static if(FiN) { 152 if(__traits(getMember, f, mem).isNull()) { 153 ret.isNull = true; 154 } else { 155 ret.value = __traits(getMember, f, mem).get(); 156 ret.isNull = false; 157 } 158 } else { 159 ret.value = __traits(getMember, f, mem); 160 ret.isNull = false; 161 } 162 return ret; 163 } 164 165 auto getValue(T)(auto ref T t) { 166 alias TnN = StripTopNullable!T; 167 struct Ret { 168 TnN value; 169 bool isNull; 170 } 171 Ret ret; 172 173 static if(isNullable!T) { 174 if(t.isNull()) { 175 ret.isNull = true; 176 } else { 177 ret.isNull = false; 178 ret.value = t.get(); 179 } 180 } else { 181 ret.isNull = false; 182 ret.value = t; 183 } 184 return ret; 185 } 186 187 void fuzzyCP(F,T)(auto ref F f, auto ref T t) { 188 static if(is(F == T)) { 189 t = f; 190 } else { 191 outer: foreach(mem; FieldNameTuple!F) { 192 static if(__traits(hasMember, T, mem)) { 193 alias FMem = typeof(__traits(getMember, F, mem)); 194 alias TMem = typeof(__traits(getMember, T, mem)); 195 alias FnN = StripNullable!FMem; 196 alias TnN = StripNullable!TMem; 197 198 enum FiS = is(FnN == struct); 199 enum TiS = is(TnN == struct); 200 201 enum FiN = isNullable!FMem; 202 enum TiN = isNullable!TMem; 203 204 enum FiA = isArray!FnN; 205 enum TiA = isArray!TnN; 206 207 enum Fd = arrayDepth!FnN; 208 enum Td = arrayDepth!TnN; 209 210 static if(FiS && TiS) { // two struct 211 fuzzyCP(__traits(getMember, f, mem), 212 __traits(getMember, t, mem) 213 ); 214 } else static if(is(FMem == TMem)) { // same types 215 __traits(getMember, t, mem) = __traits(getMember, f, mem); 216 } else static if(FiA && TiA && Fd == Td && Fd == 1) { 217 alias Tet = ElementEncodingType!(StripTopNullable!(TMem)); 218 auto arr = getValue!(TnN,F,mem)(f); 219 220 Tet[] tmp; 221 if(!arr.isNull) { 222 foreach(it; arr.value) { 223 auto itV = getValue(it); 224 if(!itV.isNull) { 225 static if(isNullable!Tet) { 226 tmp ~= nullable(itV.value); 227 } else { 228 tmp ~= itV.value; 229 } 230 } 231 } 232 } 233 static if(isNullable!(TMem)) { 234 if(!tmp.empty) { 235 __traits(getMember, t, mem) = nullable(tmp); 236 } 237 } else { 238 if(!tmp.empty) { 239 __traits(getMember, t, mem) = tmp; 240 } 241 } 242 } else static if(canBeConvertedWithTo!(FnN,TnN)) { // using to! 243 auto val = getValue!(TnN,F,mem)(f); 244 if(!val.isNull) { 245 static if(TiN) { 246 __traits(getMember, t, mem) = nullable(to!TnN(val.value)); 247 } else { 248 //writefln("%s %s", mem, fVal); 249 __traits(getMember, t, mem) = to!TMem(val.value); 250 } 251 } 252 } 253 } 254 } 255 } 256 } 257 258 unittest { 259 struct Foo { 260 int a = 10; 261 int b = 20; 262 } 263 264 struct Bar { 265 int a; 266 int b; 267 } 268 269 Foo foo; 270 assert(foo.a == 10); 271 assert(foo.b == 20); 272 273 Bar bar; 274 275 fuzzyCP(foo, bar); 276 277 assert(bar.a == 10); 278 assert(bar.b == 20); 279 } 280 281 unittest { 282 struct Foo { 283 int a = 99; 284 int b = 77; 285 } 286 287 struct Bar { 288 Foo foo; 289 string s = "hello"; 290 wstring r = "world"; 291 } 292 293 struct NotFoo { 294 long a; 295 long b; 296 } 297 298 struct NotBar { 299 NotFoo foo; 300 string s; 301 dstring r; 302 } 303 304 Bar bar; 305 NotBar nBar; 306 307 fuzzyCP(bar, nBar); 308 309 assert(nBar.foo.a == 99); 310 assert(nBar.foo.b == 77); 311 assert(nBar.s == "hello", format("'%s'", nBar.s)); 312 assert(nBar.r == "world", format("'%s'", nBar.r)); 313 } 314 315 unittest { 316 struct Foo { 317 int a = 99; 318 Nullable!int b = 77; 319 int c = 66; 320 } 321 322 struct NotFoo { 323 long a; 324 long b; 325 Nullable!byte c; 326 } 327 328 Foo foo; 329 NotFoo nFoo; 330 331 fuzzyCP(foo, nFoo); 332 333 assert(nFoo.a == 99); 334 assert(nFoo.b == 77, format("%s", nFoo.b)); 335 assert(nFoo.c.get() == 66); 336 } 337 338 unittest { 339 struct Foo { 340 int a = 99; 341 Nullable!int b = 77; 342 int c = 66; 343 int d; 344 } 345 346 struct Bar { 347 Foo[] foos; 348 } 349 350 struct NotFoo { 351 long a; 352 long b; 353 Nullable!byte c; 354 } 355 356 struct NotBar { 357 Foo[] foos; 358 } 359 360 Bar bar; 361 bar.foos = [Foo.init, Foo.init]; 362 NotBar nBar; 363 364 fuzzyCP(bar, nBar); 365 366 assert(nBar.foos.length == 2, format("%s", nBar.foos.length)); 367 } 368 369 unittest { 370 struct Foo { 371 Nullable!(int)[] i; 372 } 373 374 struct Bar { 375 int[] i; 376 } 377 378 Foo f; 379 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 380 381 Bar b; 382 fuzzyCP(f, b); 383 assert(b.i.length == 2); 384 assert(b.i == [1, 2]); 385 } 386 387 unittest { 388 struct Foo { 389 Nullable!(int)[] i; 390 } 391 392 struct Bar { 393 Nullable!(int[]) i; 394 } 395 396 Foo f; 397 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 398 399 Bar b; 400 fuzzyCP(f, b); 401 assert(!b.i.isNull()); 402 assert(b.i.get().length == 2); 403 assert(b.i.get() == [1, 2]); 404 } 405 406 unittest { 407 struct Foo { 408 Nullable!(int)[] i; 409 } 410 411 struct Bar { 412 int[] i; 413 } 414 415 Foo f; 416 417 Bar b; 418 fuzzyCP(f, b); 419 assert(b.i.length == 0); 420 } 421 422 unittest { 423 struct Foo { 424 Nullable!(Nullable!(int)[]) i; 425 } 426 427 struct Bar { 428 Nullable!(int[]) i; 429 } 430 431 Foo f; 432 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 433 434 Bar b; 435 fuzzyCP(f, b); 436 assert(!b.i.isNull()); 437 assert(b.i.get().length == 2); 438 assert(b.i.get() == [1, 2]); 439 } 440 441 template CanBeConverted(F,T) { 442 alias FnN = StripTopNullable!F; 443 alias TnN = StripTopNullable!T; 444 445 static if(is(FnN == TnN)) { 446 enum CanBeConverted = true; 447 } else static if(is(FnN == struct) && is(TnN == struct)) { 448 enum CanBeConverted = true; 449 } else static if(isSomeString!FnN && isSomeString!TnN) { 450 enum CanBeConverted = true; 451 } else static if(isArray!FnN && isArray!TnN) { 452 enum CanBeConverted = .CanBeConverted!( 453 ElementEncodingType!FnN, 454 ElementEncodingType!TnN); 455 } else static if((isIntegral!FnN || isFloatingPoint!FnN) 456 || (isIntegral!TnN || isFloatingPoint!TnN)) 457 { 458 enum CanBeConverted = true; 459 } else { 460 enum CanBeConverted = false; 461 } 462 } 463 464 auto wrap(T,Val)(Val v) { 465 static if(is(T : Nullable!F, F)) { 466 return nullable(v); 467 } else { 468 return v; 469 } 470 } 471 472 auto nullAccess(V)(V val) { 473 struct Ret { 474 475 } 476 } 477 478 T fuzzyTo(T,F)(F f) { 479 static if(is(T == F)) { 480 return f; 481 } else { 482 T ret; 483 foreach(mem; FieldNameTuple!F) { 484 static if(__traits(hasMember, T, mem)) { 485 alias FT = typeof(__traits(getMember, F, mem)); 486 alias TT = typeof(__traits(getMember, T, mem)); 487 static if(CanBeConverted!(FT, TT)) { 488 alias FnN = StripTopNullable!FT; 489 auto memVal = getValue(__traits(getMember, f, mem)); 490 if(memVal.isNull) { 491 continue; 492 } 493 494 alias TnN = StripTopNullable!TT; 495 496 static if(canBeConvertedWithTo!(FnN,TnN)) { 497 __traits(getMember, ret, mem) = wrap!TT( 498 to!TnN(memVal.value) 499 ); 500 } else static if(is(FnN == struct)) { 501 __traits(getMember, ret, mem) = wrap!TT( 502 fuzzyTo!TnN(memVal.value) 503 ); 504 } else static if(isArray!FnN) { 505 alias TET = ElementEncodingType!TnN; 506 alias TETNN = StripTopNullable!TET; 507 TET[] arr; 508 foreach(it; memVal.value) { 509 auto itVal = getValue(it); 510 if(!itVal.isNull) { 511 static if(canBeConvertedWithTo!(typeof(itVal.value),TETNN)) 512 { 513 arr ~= wrap!TET(to!TETNN(itVal.value)); 514 } else { 515 arr ~= wrap!TET(fuzzyTo!TETNN(itVal.value)); 516 } 517 } 518 } 519 static if(isNullable!TT) { 520 __traits(getMember, ret, mem) = nullable(arr); 521 } else { 522 __traits(getMember, ret, mem) = arr; 523 } 524 } 525 } 526 } 527 } 528 529 return ret; 530 } 531 } 532 533 unittest { 534 struct F { 535 int a = 13; 536 } 537 538 struct T { 539 byte a; 540 } 541 542 F f; 543 T t = fuzzyTo!T(f); 544 assert(t.a == 13); 545 546 F f2 = fuzzyTo!F(f); 547 } 548 549 unittest { 550 struct F { 551 int a = 13; 552 float[] f = [1,2,3]; 553 } 554 555 struct T { 556 byte a; 557 byte[] f; 558 } 559 560 F f; 561 T t = fuzzyTo!T(f); 562 assert(t.a == 13); 563 assert(t.f == [1,2,3]); 564 } 565 566 unittest { 567 struct F { 568 int a = 13; 569 Nullable!(float)[] f = [nullable(1.0),nullable(2.0),nullable(3.0)]; 570 } 571 572 struct T { 573 byte a; 574 byte[] f; 575 } 576 577 F f; 578 T t = fuzzyTo!T(f); 579 assert(t.a == 13); 580 assert(t.f == [1,2,3]); 581 } 582 583 unittest { 584 struct F { 585 int a = 13; 586 Nullable!(float)[] f = [nullable(1.0),nullable(2.0),nullable(3.0)]; 587 } 588 589 struct T { 590 byte a; 591 Nullable!(byte)[] f; 592 } 593 594 F f; 595 T t = fuzzyTo!T(f); 596 assert(t.a == 13); 597 assert(t.f == [1,2,3]); 598 } 599 600 unittest { 601 struct F { 602 int a = 13; 603 Nullable!(Nullable!(double)[]) f; 604 } 605 606 struct T { 607 byte a; 608 Nullable!(byte)[] f; 609 } 610 611 F f; 612 f.f = nullable([nullable(1.0),nullable(2.0),nullable(3.0)]); 613 T t = fuzzyTo!T(f); 614 assert(t.a == 13); 615 assert(t.f == [1,2,3]); 616 } 617 618 unittest { 619 struct F { 620 int a = 13; 621 Nullable!(Nullable!(double)[]) f; 622 } 623 624 struct T { 625 byte a; 626 Nullable!(byte[]) f; 627 } 628 629 F f; 630 f.f = nullable([nullable(1.0),nullable(2.0),nullable(3.0)]); 631 T t = fuzzyTo!T(f); 632 assert(t.a == 13); 633 assert(t.f == [1,2,3]); 634 } 635 636 unittest { 637 struct F { 638 int a = 13; 639 Nullable!(Nullable!(double)[]) f; 640 } 641 642 struct T { 643 byte a; 644 Nullable!(Nullable!byte[]) f; 645 } 646 647 F f; 648 f.f = nullable([nullable(1.0),Nullable!(double).init, nullable(2.0),nullable(3.0)]); 649 T t = fuzzyTo!T(f); 650 assert(t.a == 13); 651 assert(t.f == [1,2,3]); 652 } 653 654 unittest { 655 struct A { 656 float a = 10; 657 } 658 659 struct F { 660 A a; 661 } 662 663 struct B { 664 int a; 665 } 666 667 struct T { 668 B a; 669 } 670 671 F f; 672 T t = fuzzyTo!T(f); 673 assert(t.a.a == 10); 674 } 675 676 unittest { 677 struct A { 678 float a = 10; 679 } 680 681 struct F { 682 Nullable!A a = nullable(A.init); 683 } 684 685 struct B { 686 int a; 687 } 688 689 struct T { 690 B a; 691 } 692 693 F f; 694 T t = fuzzyTo!T(f); 695 assert(t.a.a == 10); 696 } 697 698 unittest { 699 struct A { 700 Nullable!float a = nullable(10.0); 701 } 702 703 struct F { 704 Nullable!A a = nullable(A.init); 705 } 706 707 struct B { 708 int a; 709 } 710 711 struct T { 712 B a; 713 } 714 715 F f; 716 T t = fuzzyTo!T(f); 717 assert(t.a.a == 10); 718 } 719 720 unittest { 721 struct A { 722 Nullable!float a = nullable(10.0); 723 } 724 725 struct F { 726 Nullable!(A[]) a = nullable([A.init, A.init]); 727 } 728 729 struct B { 730 int a; 731 } 732 733 struct T { 734 B[] a; 735 } 736 737 F f; 738 T t = fuzzyTo!T(f); 739 assert(t.a.map!(it => it.a).array == [10, 10]); 740 } 741 742 unittest { 743 struct A { 744 Nullable!float a; 745 } 746 747 struct F { 748 Nullable!(A[]) a = nullable([A.init, A.init]); 749 Nullable!(A[]) b; 750 } 751 752 struct B { 753 int a; 754 } 755 756 struct T { 757 B[] a; 758 B[] b; 759 } 760 761 F f; 762 T t = fuzzyTo!T(f); 763 assert(t.a.length == 2); 764 assert(t.b.empty); 765 }