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 outer: foreach(mem; FieldNameTuple!F) { 189 static if(__traits(hasMember, T, mem)) { 190 alias FMem = typeof(__traits(getMember, F, mem)); 191 alias TMem = typeof(__traits(getMember, T, mem)); 192 alias FnN = StripNullable!FMem; 193 alias TnN = StripNullable!TMem; 194 195 enum FiS = is(FnN == struct); 196 enum TiS = is(TnN == struct); 197 198 enum FiN = isNullable!FMem; 199 enum TiN = isNullable!TMem; 200 201 enum FiA = isArray!FnN; 202 enum TiA = isArray!TnN; 203 204 enum Fd = arrayDepth!FnN; 205 enum Td = arrayDepth!TnN; 206 207 static if(FiS && TiS) { // two struct 208 fuzzyCP(__traits(getMember, f, mem), 209 __traits(getMember, t, mem) 210 ); 211 } else static if(is(FMem == TMem)) { // same types 212 __traits(getMember, t, mem) = __traits(getMember, f, mem); 213 } else static if(FiA && TiA && Fd == Td && Fd == 1) { 214 alias Tet = ElementEncodingType!(StripTopNullable!(TMem)); 215 auto arr = getValue!(TnN,F,mem)(f); 216 217 Tet[] tmp; 218 if(!arr.isNull) { 219 foreach(it; arr.value) { 220 auto itV = getValue(it); 221 if(!itV.isNull) { 222 static if(isNullable!Tet) { 223 tmp ~= nullable(itV.value); 224 } else { 225 tmp ~= itV.value; 226 } 227 } 228 } 229 } 230 static if(isNullable!(TMem)) { 231 if(!tmp.empty) { 232 __traits(getMember, t, mem) = nullable(tmp); 233 } 234 } else { 235 if(!tmp.empty) { 236 __traits(getMember, t, mem) = tmp; 237 } 238 } 239 } else static if(canBeConvertedWithTo!(FnN,TnN)) { // using to! 240 auto val = getValue!(TnN,F,mem)(f); 241 if(!val.isNull) { 242 static if(TiN) { 243 __traits(getMember, t, mem) = nullable(to!TnN(val.value)); 244 } else { 245 //writefln("%s %s", mem, fVal); 246 __traits(getMember, t, mem) = to!TMem(val.value); 247 } 248 } 249 } 250 } 251 } 252 } 253 254 unittest { 255 struct Foo { 256 int a = 10; 257 int b = 20; 258 } 259 260 struct Bar { 261 int a; 262 int b; 263 } 264 265 Foo foo; 266 assert(foo.a == 10); 267 assert(foo.b == 20); 268 269 Bar bar; 270 271 fuzzyCP(foo, bar); 272 273 assert(bar.a == 10); 274 assert(bar.b == 20); 275 } 276 277 unittest { 278 struct Foo { 279 int a = 99; 280 int b = 77; 281 } 282 283 struct Bar { 284 Foo foo; 285 string s = "hello"; 286 wstring r = "world"; 287 } 288 289 struct NotFoo { 290 long a; 291 long b; 292 } 293 294 struct NotBar { 295 NotFoo foo; 296 string s; 297 dstring r; 298 } 299 300 Bar bar; 301 NotBar nBar; 302 303 fuzzyCP(bar, nBar); 304 305 assert(nBar.foo.a == 99); 306 assert(nBar.foo.b == 77); 307 assert(nBar.s == "hello", format("'%s'", nBar.s)); 308 assert(nBar.r == "world", format("'%s'", nBar.r)); 309 } 310 311 unittest { 312 struct Foo { 313 int a = 99; 314 Nullable!int b = 77; 315 int c = 66; 316 } 317 318 struct NotFoo { 319 long a; 320 long b; 321 Nullable!byte c; 322 } 323 324 Foo foo; 325 NotFoo nFoo; 326 327 fuzzyCP(foo, nFoo); 328 329 assert(nFoo.a == 99); 330 assert(nFoo.b == 77, format("%s", nFoo.b)); 331 assert(nFoo.c.get() == 66); 332 } 333 334 unittest { 335 struct Foo { 336 int a = 99; 337 Nullable!int b = 77; 338 int c = 66; 339 } 340 341 struct Bar { 342 Foo[] foos; 343 } 344 345 struct NotFoo { 346 long a; 347 long b; 348 Nullable!byte c; 349 } 350 351 struct NotBar { 352 Foo[] foos; 353 } 354 355 Bar bar; 356 bar.foos = [Foo.init, Foo.init]; 357 NotBar nBar; 358 359 fuzzyCP(bar, nBar); 360 361 assert(nBar.foos.length == 2, format("%s", nBar.foos.length)); 362 } 363 364 unittest { 365 struct Foo { 366 Nullable!(int)[] i; 367 } 368 369 struct Bar { 370 int[] i; 371 } 372 373 Foo f; 374 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 375 376 Bar b; 377 fuzzyCP(f, b); 378 assert(b.i.length == 2); 379 assert(b.i == [1, 2]); 380 } 381 382 unittest { 383 struct Foo { 384 Nullable!(int)[] i; 385 } 386 387 struct Bar { 388 Nullable!(int[]) i; 389 } 390 391 Foo f; 392 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 393 394 Bar b; 395 fuzzyCP(f, b); 396 assert(!b.i.isNull()); 397 assert(b.i.get().length == 2); 398 assert(b.i.get() == [1, 2]); 399 } 400 401 unittest { 402 struct Foo { 403 Nullable!(int)[] i; 404 } 405 406 struct Bar { 407 int[] i; 408 } 409 410 Foo f; 411 412 Bar b; 413 fuzzyCP(f, b); 414 assert(b.i.length == 0); 415 } 416 417 unittest { 418 struct Foo { 419 Nullable!(Nullable!(int)[]) i; 420 } 421 422 struct Bar { 423 Nullable!(int[]) i; 424 } 425 426 Foo f; 427 f.i = [nullable(1), nullable(2), Nullable!(int).init]; 428 429 Bar b; 430 fuzzyCP(f, b); 431 assert(!b.i.isNull()); 432 assert(b.i.get().length == 2); 433 assert(b.i.get() == [1, 2]); 434 } 435 436 template CanBeConverted(F,T) { 437 alias FnN = StripTopNullable!F; 438 alias TnN = StripTopNullable!T; 439 440 static if(is(FnN == TnN)) { 441 enum CanBeConverted = true; 442 } else static if(is(FnN == struct) && is(TnN == struct)) { 443 enum CanBeConverted = true; 444 } else static if(isSomeString!FnN && isSomeString!TnN) { 445 enum CanBeConverted = true; 446 } else static if(isArray!FnN && isArray!TnN) { 447 enum CanBeConverted = .CanBeConverted!( 448 ElementEncodingType!FnN, 449 ElementEncodingType!TnN); 450 } else static if((isIntegral!FnN || isFloatingPoint!FnN) 451 || (isIntegral!TnN || isFloatingPoint!TnN)) 452 { 453 enum CanBeConverted = true; 454 } else { 455 enum CanBeConverted = false; 456 } 457 } 458 459 auto wrap(T,Val)(Val v) { 460 static if(is(T : Nullable!F, F)) { 461 return nullable(v); 462 } else { 463 return v; 464 } 465 } 466 467 auto nullAccess(V)(V val) { 468 struct Ret { 469 470 } 471 } 472 473 T fuzzyTo(T,F)(F f) { 474 T ret; 475 foreach(mem; FieldNameTuple!F) { 476 static if(__traits(hasMember, T, mem)) { 477 alias FT = typeof(__traits(getMember, F, mem)); 478 alias TT = typeof(__traits(getMember, T, mem)); 479 static if(CanBeConverted!(FT, TT)) { 480 alias FnN = StripTopNullable!FT; 481 auto memVal = getValue(__traits(getMember, f, mem)); 482 if(memVal.isNull) { 483 continue; 484 } 485 486 alias TnN = StripTopNullable!TT; 487 488 static if(canBeConvertedWithTo!(FnN,TnN)) { 489 __traits(getMember, ret, mem) = wrap!TT( 490 to!TnN(memVal.value) 491 ); 492 } else static if(is(FnN == struct)) { 493 __traits(getMember, ret, mem) = wrap!TT( 494 fuzzyTo!TnN(memVal.value) 495 ); 496 } else static if(isArray!FnN) { 497 alias TET = ElementEncodingType!TnN; 498 alias TETNN = StripTopNullable!TET; 499 TET[] arr; 500 foreach(it; memVal.value) { 501 auto itVal = getValue(it); 502 if(!itVal.isNull) { 503 static if(canBeConvertedWithTo!(typeof(itVal.value),TETNN)) 504 { 505 arr ~= wrap!TET(to!TETNN(itVal.value)); 506 } else { 507 arr ~= wrap!TET(fuzzyTo!TETNN(itVal.value)); 508 } 509 } 510 } 511 static if(isNullable!TT) { 512 __traits(getMember, ret, mem) = nullable(arr); 513 } else { 514 __traits(getMember, ret, mem) = arr; 515 } 516 } 517 } 518 } 519 } 520 521 return ret; 522 } 523 524 unittest { 525 struct F { 526 int a = 13; 527 } 528 529 struct T { 530 byte a; 531 } 532 533 F f; 534 T t = fuzzyTo!T(f); 535 assert(t.a == 13); 536 } 537 538 unittest { 539 struct F { 540 int a = 13; 541 float[] f = [1,2,3]; 542 } 543 544 struct T { 545 byte a; 546 byte[] f; 547 } 548 549 F f; 550 T t = fuzzyTo!T(f); 551 assert(t.a == 13); 552 assert(t.f == [1,2,3]); 553 } 554 555 unittest { 556 struct F { 557 int a = 13; 558 Nullable!(float)[] f = [nullable(1.0),nullable(2.0),nullable(3.0)]; 559 } 560 561 struct T { 562 byte a; 563 byte[] f; 564 } 565 566 F f; 567 T t = fuzzyTo!T(f); 568 assert(t.a == 13); 569 assert(t.f == [1,2,3]); 570 } 571 572 unittest { 573 struct F { 574 int a = 13; 575 Nullable!(float)[] f = [nullable(1.0),nullable(2.0),nullable(3.0)]; 576 } 577 578 struct T { 579 byte a; 580 Nullable!(byte)[] f; 581 } 582 583 F f; 584 T t = fuzzyTo!T(f); 585 assert(t.a == 13); 586 assert(t.f == [1,2,3]); 587 } 588 589 unittest { 590 struct F { 591 int a = 13; 592 Nullable!(Nullable!(double)[]) f; 593 } 594 595 struct T { 596 byte a; 597 Nullable!(byte)[] f; 598 } 599 600 F f; 601 f.f = nullable([nullable(1.0),nullable(2.0),nullable(3.0)]); 602 T t = fuzzyTo!T(f); 603 assert(t.a == 13); 604 assert(t.f == [1,2,3]); 605 } 606 607 unittest { 608 struct F { 609 int a = 13; 610 Nullable!(Nullable!(double)[]) f; 611 } 612 613 struct T { 614 byte a; 615 Nullable!(byte[]) f; 616 } 617 618 F f; 619 f.f = nullable([nullable(1.0),nullable(2.0),nullable(3.0)]); 620 T t = fuzzyTo!T(f); 621 assert(t.a == 13); 622 assert(t.f == [1,2,3]); 623 } 624 625 unittest { 626 struct F { 627 int a = 13; 628 Nullable!(Nullable!(double)[]) f; 629 } 630 631 struct T { 632 byte a; 633 Nullable!(Nullable!byte[]) f; 634 } 635 636 F f; 637 f.f = nullable([nullable(1.0),Nullable!(double).init, nullable(2.0),nullable(3.0)]); 638 T t = fuzzyTo!T(f); 639 assert(t.a == 13); 640 assert(t.f == [1,2,3]); 641 } 642 643 unittest { 644 struct A { 645 float a = 10; 646 } 647 648 struct F { 649 A a; 650 } 651 652 struct B { 653 int a; 654 } 655 656 struct T { 657 B a; 658 } 659 660 F f; 661 T t = fuzzyTo!T(f); 662 assert(t.a.a == 10); 663 } 664 665 unittest { 666 struct A { 667 float a = 10; 668 } 669 670 struct F { 671 Nullable!A a = nullable(A.init); 672 } 673 674 struct B { 675 int a; 676 } 677 678 struct T { 679 B a; 680 } 681 682 F f; 683 T t = fuzzyTo!T(f); 684 assert(t.a.a == 10); 685 } 686 687 unittest { 688 struct A { 689 Nullable!float a = nullable(10.0); 690 } 691 692 struct F { 693 Nullable!A a = nullable(A.init); 694 } 695 696 struct B { 697 int a; 698 } 699 700 struct T { 701 B a; 702 } 703 704 F f; 705 T t = fuzzyTo!T(f); 706 assert(t.a.a == 10); 707 } 708 709 unittest { 710 struct A { 711 Nullable!float a = nullable(10.0); 712 } 713 714 struct F { 715 Nullable!(A[]) a = nullable([A.init, A.init]); 716 } 717 718 struct B { 719 int a; 720 } 721 722 struct T { 723 B[] a; 724 } 725 726 F f; 727 T t = fuzzyTo!T(f); 728 assert(t.a.map!(it => it.a).array == [10, 10]); 729 } 730 731 unittest { 732 struct A { 733 Nullable!float a; 734 } 735 736 struct F { 737 Nullable!(A[]) a = nullable([A.init, A.init]); 738 Nullable!(A[]) b; 739 } 740 741 struct B { 742 int a; 743 } 744 745 struct T { 746 B[] a; 747 B[] b; 748 } 749 750 F f; 751 T t = fuzzyTo!T(f); 752 assert(t.a.length == 2); 753 assert(t.b.empty); 754 }