1 module virc.numerics.magicparser;
2 
3 import std.datetime : UTC;
4 import std.range : isInputRange, ElementType;
5 import std.traits : hasUDA;
6 import std.typecons : Nullable;
7 
8 enum Optional;
9 
10 enum isOptional(T, string member) = hasUDA!(__traits(getMember, T, member), Optional);
11 
12 template autoParse(T...) {
13 	MostCommon!T autoParse(Range)(Range input) if (isInputRange!Range && is(ElementType!Range == string)){
14 		import core.time : Duration, seconds;
15 		import std.array : empty, front, popFront;
16 		import std.conv : to;
17 		import std.datetime : SysTime;
18 		import std.traits : FieldNameTuple, hasIndirections, hasUDA;
19 		MostCommon!T output;
20 		static foreach (Type; T) {{
21 			auto attempt = tryParse!Type(input);
22 			if (!attempt.isNull) {
23 				output = MostCommon!T(attempt.get);
24 				return output;
25 			}
26 		}}
27 		return output;
28 	}
29 }
30 Nullable!T tryParse(T, Range)(Range input) if (isInputRange!Range && is(ElementType!Range == string)){
31 	import core.time : Duration, seconds;
32 	import std.array : empty, front, popFront;
33 	import std.conv : to;
34 	import std.datetime : SysTime;
35 	import std.traits : FieldNameTuple, hasIndirections, hasUDA;
36 	Nullable!T output = T.init;
37 	static immutable utc = UTC();
38 	foreach (member; FieldNameTuple!T) {
39 		alias MemberType = typeof(__traits(getMember, T, member));
40 		if (input.empty) {
41 			static if (isOptional!(T, member)) {
42 				continue;
43 			} else {
44 				return Nullable!T.init;
45 			}
46 		}
47 		static if (is(MemberType == SysTime)) {
48 			try {
49 				__traits(getMember, output.get, member) = SysTime.fromUnixTime(input.front.to!ulong, utc);
50 			} catch (Exception) {
51 				static if (isOptional!(T, member)) {
52 					continue;
53 				} else {
54 					return Nullable!T.init;
55 				}
56 			}
57 		} else static if (is(MemberType == Duration)) {
58 			try {
59 				__traits(getMember, output.get, member) = input.front.to!ulong.seconds;
60 			} catch (Exception) {
61 				static if (isOptional!(T, member)) {
62 					continue;
63 				} else {
64 					return Nullable!T.init;
65 				}
66 			}
67 		} else {
68 			__traits(getMember, output.get, member) = input.front.to!MemberType;
69 		}
70 		input.popFront();
71 	}
72 	return output;
73 }
74 template MostCommon(Types...) {
75 	static if (Types.length == 1) {
76 		alias MostCommon = Nullable!(Types[0]);
77 	} else {
78 		struct MostCommon {
79 			import std.conv : text;
80 			import std.meta : ApplyRight, Filter, NoDuplicates, staticMap;
81 			import std.string : toLower;
82 			import std.traits : hasMember;
83 			import std.typecons : Nullable;
84 			static foreach (i, Type; Types) {
85 				mixin("Nullable!(Types["~i.text~"]) " ~ Type.stringof.toLower()~";");
86 				void opAssign(Type input) {
87 					mixin(Type.stringof.toLower()~" = input;");
88 				}
89 				this(Type input) {
90 					mixin(Type.stringof.toLower()~" = input;");
91 				}
92 			}
93 			auto opDispatch(string member)() const {
94 				alias HasMemberTypes = Filter!(ApplyRight!(hasMember, member), Types);
95 				alias subMemberType(T) = typeof(__traits(getMember, T, member));
96 				static assert(HasMemberTypes.length > 0, member~" not found in any of the provided types!");
97 				static assert(NoDuplicates!(staticMap!(subMemberType, HasMemberTypes)).length == 1, member~" does not have a common type in all provided types!");
98 				alias FinalType = typeof(__traits(getMember, HasMemberTypes[0], member));
99 
100 				Nullable!FinalType output;
101 				static foreach(SubType; HasMemberTypes) {
102 					if (!__traits(getMember, this, SubType.stringof.toLower()).isNull) {
103 						output = __traits(getMember, __traits(getMember, this, SubType.stringof.toLower()).get, member);
104 					}
105 				}
106 				return output;
107 			}
108 			bool isNull() const {
109 				static foreach(SubType; Types) {
110 					if (!__traits(getMember, this, SubType.stringof.toLower()).isNull) {
111 						return false;
112 					}
113 				}
114 				return true;
115 			}
116 		}
117 	}
118 }
119 ///
120 unittest {
121 	struct A {
122 		uint c;
123 		uint x;
124 		string y;
125 	}
126 	struct B {
127 		string c;
128 		uint x;
129 		uint z;
130 	}
131 	MostCommon!(A, B) test;
132 	test.a = A(0, 1, "b");
133 	assert(test.x == 1);
134 	assert(test.z.isNull);
135 }