1 module toml_foolery.decode.set_data; 2 3 import std.conv; 4 import std.datetime; 5 import std.traits; 6 import std.range.primitives; 7 8 import toml_foolery.attributes.toml_name; 9 import toml_foolery.decode.exceptions; 10 11 12 /// A magical function which puts `value` into `dest`, inside the field indicated by `address`. 13 package void setData(S, T)(ref S dest, string[] address, const T value) 14 if (is(S == struct)) 15 in (address.length > 0, "`address` may not be empty") 16 { 17 switch (address[0]) 18 { 19 // For each member of S... 20 static foreach (string member; __traits(allMembers, S)) 21 { 22 // ... that isn't "this", isn't a non-property function, and is public, and... 23 static if ( 24 member == "this" || 25 ( 26 isCallable!(__traits(getMember, dest, member)) && 27 !hasFunctionAttributes!(__traits(getMember, dest, member), "@property") 28 ) || 29 __traits(getProtection, __traits(getMember, dest, member)) != "public" 30 ) 31 { 32 // do nothing 33 } 34 else 35 { 36 // ...that isn't a nested struct declaration... 37 static if (!is(__traits(getMember, dest, member))) 38 { 39 case dFieldToTomlKey!(S, member): 40 { 41 static assert( 42 !( 43 isCallable!(__traits(getMember, S, member)) && 44 is(ReturnType!(__traits(getMember, S, member)) == struct) && 45 hasFunctionAttributes!(__traits(getMember, S, member), "@property") 46 ), 47 `Field "` ~ member ~ `" of struct "` ~ S.stringof ~ `" is a public property. ` ~ 48 `Make it private to ignore it, or make it a regular field to allow placing data inside.` 49 ); 50 51 if (address.length == 1) 52 { 53 static if ( 54 __traits( 55 compiles, 56 __traits(getMember, dest, member) = value.to!(typeof(__traits(getMember, dest, member))) 57 ) 58 ) 59 { 60 try 61 { 62 __traits(getMember, dest, member) = value.to!(typeof(__traits(getMember, dest, member))); 63 } 64 catch (ConvOverflowException e) 65 { 66 throw new TomlTypeException( 67 `Key "` ~ dFieldToTomlKey!(S, member) ~ `"` ~ 68 ` has value ` ~ value.to!string ~ ` which cannot fit in field ` ~ 69 S.stringof ~ `.` ~ member ~ 70 ` of type ` ~ typeof(__traits(getMember, dest, member)).stringof, 71 e 72 ); 73 } 74 return; 75 } 76 else static if (__traits(compiles, typeof(__traits(getMember, dest, member)))) 77 { 78 throw new TomlTypeException( 79 `Member "` ~ member ~ `" of struct "` ~ S.stringof ~ 80 `" is of type "` ~ typeof(__traits(getMember, dest, member)).stringof ~ 81 `", but given value is type "` ~ typeof(value).stringof ~ `".` 82 ); 83 } 84 else 85 { 86 throw new TomlDecodingException(`Member "` ~ member ~ `" is not a valid destination.`); 87 } 88 } 89 else 90 { 91 // ...is a struct or array (allowing a recursive call), but isn't a @property 92 // (since those return structs as rvalues which cannot be passed as ref) 93 94 // Do nothing if it's a specially-handled struct. 95 static if ( 96 (is(typeof(__traits(getMember, dest, member)) == TimeOfDay)) || 97 (is(typeof(__traits(getMember, dest, member)) == SysTime)) || 98 (is(typeof(__traits(getMember, dest, member)) == DateTime)) || 99 (is(typeof(__traits(getMember, dest, member)) == Date)) 100 ) 101 { 102 return; 103 } 104 else static if(__traits(compiles, setData(__traits(getMember, dest, member), address[1..$], value))) 105 { 106 setData! 107 (typeof(__traits(getMember, dest, member)), T) 108 (__traits(getMember, dest, member), address[1..$], value); 109 110 return; 111 } 112 else 113 { 114 throw new TomlDecodingException( 115 `Could not place value "` ~ value.to!string ~ 116 `" of type "` ~ T.stringof ~ 117 `" inside field "` ~ member ~ 118 `" of type "` ~ typeof(__traits(getMember, S, member)).stringof ~ 119 `" in struct "` ~ S.stringof ~ `". This might be a bug — please file a report.` 120 ); 121 } 122 } 123 } 124 } 125 } 126 } 127 128 default: 129 break; 130 } 131 132 } 133 134 /// ditto 135 package void setData(S, T)(ref S dest, string[] address, const T value) 136 if (isArray!S) 137 in (address.length > 0, "`address` may not be empty") 138 in (address[0].isSizeT, `address[0] = "` ~ address[0] ~ `" which is not convertible to size_t.`) 139 { 140 size_t idx = address[0].to!size_t; 141 if (idx >= dest.length) 142 { 143 static if (isStaticArray!S) 144 { 145 // ignore 146 return; 147 } 148 else 149 { 150 static assert(isDynamicArray!S, "Encountered an array that is neither static nor dynamic (???)"); 151 dest.length = idx + 1; 152 } 153 } 154 155 if (address.length == 1) 156 { 157 // Defined as a function so that it can go inside a __traits(compiles, ...) 158 static void setIndex(S, T)(ref S dest, size_t idx, T value) 159 { 160 dest[idx] = value; 161 } 162 163 static if (__traits(compiles, setIndex(dest, idx, value.to!(ElementType!S)))) 164 { 165 setIndex(dest, idx, value.to!(ElementType!S)); 166 } 167 else 168 { 169 assert ( 170 false, 171 `Invalid operation: ` ~ 172 dest.to!string ~ `[` ~ idx.to!string ~ `] = 173 ` ~ value.to!string ~ `.to!` ~ (ElementType!S).stringof 174 ); 175 } 176 } 177 else 178 { 179 static if (isArray!(typeof(dest[idx])) || is(typeof(dest[idx]) == struct)) 180 { 181 // Do nothing if it's a specially-handled struct. 182 static if ( 183 !(is(typeof(dest[idx]) == TimeOfDay)) && 184 !(is(typeof(dest[idx]) == SysTime)) && 185 !(is(typeof(dest[idx]) == DateTime)) && 186 !(is(typeof(dest[idx]) == Date)) 187 ) 188 { 189 setData(dest[idx], address[1 .. $], value); 190 } 191 } 192 } 193 } 194 195 debug 196 { 197 private bool isSizeT(string s) 198 { 199 import std.conv : ConvException; 200 201 try 202 { 203 s.to!size_t; 204 return true; 205 } 206 catch (ConvException e) 207 { 208 return false; 209 } 210 } 211 212 @("isSizeT") 213 unittest 214 { 215 import std.bigint; 216 import dshould; 217 size_t.min.to!string.isSizeT.should.equal(true); 218 size_t.max.to!string.isSizeT.should.equal(true); 219 (BigInt(size_t.max) + 1).to!string.isSizeT.should.equal(false); 220 (-1).to!string.isSizeT.should.equal(false); 221 } 222 }