1 module dud.sdlang.value; 2 3 import std.datetime : DateTime, Duration, Date; 4 5 /// DateTime doesn't support milliseconds, but SDL's "Date Time" type does. 6 /// So this is needed for any SDL "Date Time" that doesn't include a time zone. 7 struct DateTimeFrac { 8 @safe pure: 9 this(DateTime dt, Duration fs) { 10 this.dateTime = dt; 11 this.fracSecs = fs; 12 } 13 14 this(DateTime dt) { 15 this.dateTime = dt; 16 } 17 18 DateTime dateTime; 19 Duration fracSecs; 20 } 21 22 /++ 23 If a "Date Time" literal in the SDL file has a time zone that's not found in 24 your system, you get one of these instead of a SysTime. (Because it's 25 impossible to indicate "unknown time zone" with 'std.datetime.TimeZone'.) 26 27 The difference between this and 'DateTimeFrac' is that 'DateTimeFrac' 28 indicates that no time zone was specified in the SDL at all, whereas 29 'DateTimeFracUnknownZone' indicates that a time zone was specified but 30 data for it could not be found on your system. 31 +/ 32 struct DateTimeFracUnknownZone { 33 @safe pure: 34 DateTime dateTime; 35 Duration fracSecs; 36 string timeZone; 37 38 bool opEquals(const DateTimeFracUnknownZone b) const { 39 return opEquals(b); 40 } 41 42 bool opEquals(ref const DateTimeFracUnknownZone b) const { 43 return this.dateTime == b.dateTime && this.fracSecs == b.fracSecs 44 && this.timeZone == b.timeZone; 45 } 46 } 47 48 enum ValueType { 49 boolean, 50 int32, 51 int64, 52 float32, 53 float64, 54 float128, 55 decimal128, 56 date, 57 datetime, 58 datetimeTZ, 59 duration, 60 binary, 61 str, 62 char_, 63 null_ 64 } 65 66 private union Data { 67 long integral; 68 double floating64; 69 real floating128; 70 bool boolean; 71 Duration duration; 72 Date date; 73 DateTimeFrac datetime; // datetimeTZ 74 DateTimeFracUnknownZone datetimeUZ; //datetime 75 ubyte[] binary; 76 string str; 77 dchar character; 78 } 79 80 struct Value { 81 @safe pure: 82 ValueType type = ValueType.null_; 83 Data data; 84 85 this(Duration i) @trusted { 86 this.type = ValueType.duration; 87 this.data.duration = i; 88 } 89 90 this(dchar i) @trusted { 91 this.type = ValueType.char_; 92 this.data.character = i; 93 } 94 95 this(string i) @trusted { 96 this.type = ValueType.str; 97 this.data.str = i; 98 } 99 100 this(ubyte[] i) @trusted { 101 this.type = ValueType.binary; 102 this.data.binary = i; 103 } 104 105 this(DateTimeFracUnknownZone i) @trusted { 106 this.type = ValueType.datetime; 107 this.data.datetimeUZ = i; 108 } 109 110 this(DateTimeFrac i) @trusted { 111 this.type = ValueType.datetimeTZ; 112 this.data.datetime = i; 113 } 114 115 this(Date i) @trusted { 116 this.type = ValueType.date; 117 this.data.date = i; 118 } 119 120 this(bool i) @trusted { 121 this.type = ValueType.boolean; 122 this.data.boolean = i; 123 } 124 125 this(int i) @trusted { 126 this.type = ValueType.int32; 127 this.data.integral = i; 128 } 129 130 this(long i) @trusted { 131 this.type = ValueType.int64; 132 this.data.integral = i; 133 } 134 135 this(float i) @trusted { 136 this.type = ValueType.float32; 137 this.data.floating64 = i; 138 } 139 140 this(double i) @trusted { 141 this.type = ValueType.float64; 142 this.data.floating64 = i; 143 } 144 145 this(real i) @trusted { 146 this.type = ValueType.float128; 147 this.data.floating128 = i; 148 } 149 150 T get(T)() const pure @safe { 151 import std.algorithm.searching : canFind; 152 import std.conv : to; 153 import std.exception : enforce; 154 import std.traits : isFloatingPoint, isIntegral, isSomeString; 155 import std.format : format; 156 static if(isSomeString!T) { 157 enforce(this.type == ValueType.str, 158 format("Can not get '%s' when the type is '%s'", T.stringof, 159 this.type)); 160 return () @trusted { return this.data.str; }(); 161 } else static if(is(T == ubyte[])) { 162 enforce(this.type == ValueType.binary, 163 format("Can not get '%s' when the type is '%s'", T.stringof, 164 this.type)); 165 return this.data.binary; 166 } else static if(is(T == dchar)) { 167 enforce(this.type == ValueType.char_, 168 format("Can not get '%s' when the type is '%s'", T.stringof, 169 this.type)); 170 return this.data.character; 171 } else static if(is(T == Duration)) { 172 enforce(this.type == ValueType.duration, 173 format("Can not get '%s' when the type is '%s'", T.stringof, 174 this.type)); 175 return this.data.duration; 176 } else static if(is(T == DateTimeFrac)) { 177 enforce(this.type == ValueType.datetimeTZ, 178 format("Can not get '%s' when the type is '%s'", T.stringof, 179 this.type)); 180 return this.data.datetime; 181 } else static if(is(T == DateTimeFracUnknownZone)) { 182 enforce(this.type == ValueType.datetime, 183 format("Can not get '%s' when the type is '%s'", T.stringof, 184 this.type)); 185 return this.data.datetimeUZ; 186 } else static if(is(T == bool)) { 187 enforce(this.type == ValueType.boolean, 188 format("Can not get '%s' when the type is '%s'", T.stringof, 189 this.type)); 190 return this.data.boolean; 191 } else static if(isIntegral!T) { 192 enum tt = 193 [ ValueType.int32, ValueType.int64, ValueType.float32 ]; 194 enforce(canFind(tt, this.type), 195 format("Can not get '%s' when the type is '%s'", T.stringof, 196 this.type)); 197 switch(this.type) { 198 case ValueType.int32: 199 goto case; 200 case ValueType.int64: 201 return to!T(this.data.integral); 202 default: 203 assert(false, "We should never reach this"); 204 } 205 assert(false, "We should never reach either"); 206 } else static if(isFloatingPoint!T) { 207 enum tt = 208 [ ValueType.int32, ValueType.int64, ValueType.float32 209 , ValueType.float64, ValueType.float128, ValueType.decimal128 210 ]; 211 212 enforce(canFind(tt, this.type), 213 format("Can not get '%s' when the type is '%s'", T.stringof, 214 this.type)); 215 switch(this.type) { 216 case ValueType.int32: 217 goto case; 218 case ValueType.int64: 219 return to!T(this.data.integral); 220 case ValueType.float32: 221 goto case; 222 case ValueType.float64: 223 return to!T(this.data.floating64); 224 case ValueType.float128: 225 return to!T(this.data.floating128); 226 case ValueType.decimal128: 227 return to!T(this.data.floating128); 228 default: 229 assert(false, "We should never reach this"); 230 } 231 assert(false, "We should never reach either"); 232 } else { 233 static assert(false, format("getting a %s is not handled", 234 T.stringof)); 235 } 236 } 237 }