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 }