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 }