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