1 /** 2 * Type serializer and deserializer. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.serialization.serialization; 15 16 import std.conv; 17 import std.format; 18 import std.string; 19 import std.traits; 20 21 import ae.utils.meta; 22 import ae.utils.text; 23 24 /// Serialization source which serializes a given object. 25 struct Serializer 26 { 27 static template Impl(alias anchor) 28 { 29 static void read(Sink, T)(Sink sink, auto ref T v) 30 { 31 static if (is(typeof(v is null))) 32 if (v is null) 33 { 34 sink.handleNull(); 35 return; 36 } 37 38 static if (is(T == bool)) 39 sink.handleBoolean(v); 40 else 41 static if (is(T : ulong)) 42 { 43 char[DecimalSize!T] buf = void; 44 sink.handleNumeric(toDec(v, buf)); 45 } 46 else 47 static if (isNumeric!T) // floating point 48 { 49 import ae.utils.textout; 50 51 static char[64] arr; 52 auto buf = StringBuffer(arr); 53 formattedWrite(&buf, "%s", v); 54 sink.handleNumeric(buf.get()); 55 } 56 else 57 static if (is(T == struct)) 58 { 59 auto reader = StructReader!T(v.reference); 60 sink.handleObject(boundFunctorOf!(StructReader!T.read)(&reader)); 61 } 62 else 63 static if (is(T V : V[K], K)) 64 { 65 alias Reader = AAReader!(T, K, V); 66 auto reader = Reader(v); 67 sink.handleObject(boundFunctorOf!(Reader.read)(&reader)); 68 } 69 else 70 static if (is(T : string)) 71 sink.handleString(v); 72 else 73 static if (is(T U : U[])) 74 { 75 alias Reader = ArrayReader!T; 76 auto reader = Reader(v); 77 sink.handleArray(boundFunctorOf!(Reader.readArray)(&reader)); 78 } 79 else 80 static assert(false, "Don't know how to serialize " ~ T.stringof); 81 } 82 83 static struct StructReader(T) 84 { 85 RefType!T p; 86 void read(Sink)(Sink sink) 87 { 88 foreach (i, ref field; p.dereference.tupleof) 89 { 90 import std.array : split; 91 enum name = p.dereference.tupleof[i].stringof.split(".")[$-1]; 92 93 alias ValueReader = Reader!(typeof(field)); 94 auto reader = ValueReader(&field); 95 sink.handleField(unboundFunctorOf!(stringReader!name), boundFunctorOf!(ValueReader.readValue)(&reader)); 96 } 97 } 98 } 99 100 static struct AAReader(T, K, V) 101 { 102 T aa; 103 void read(Sink)(Sink sink) 104 { 105 foreach (K k, ref V v; aa) 106 { 107 alias KeyReader = Reader!K; 108 auto keyReader = KeyReader (&k); 109 alias ValueReader = Reader!V; 110 auto valueReader = ValueReader(&v); 111 sink.handleField( 112 boundFunctorOf!(KeyReader .readValue)(&keyReader ), 113 boundFunctorOf!(ValueReader.readValue)(&valueReader), 114 ); 115 } 116 } 117 } 118 119 static struct ArrayReader(T) 120 { 121 T arr; 122 void readArray(Sink)(Sink sink) 123 { 124 foreach (ref v; arr) 125 read(sink, v); 126 } 127 } 128 129 static template stringReader(string name) 130 { 131 static void stringReader(Sink)(Sink sink) 132 { 133 sink.handleString(name); 134 } 135 } 136 137 static struct Reader(T) 138 { 139 T* p; 140 141 void readValue(Sink)(Sink sink) 142 { 143 read(sink, *p); 144 } 145 } 146 } 147 } 148 149 /// Serialization sink which deserializes into a given type. 150 template Deserializer(alias anchor) 151 { 152 alias C = immutable(char); // TODO 153 154 mixin template SinkHandlers(T) 155 { 156 template unparseable(string inputType) 157 { 158 void unparseable(Reader)(Reader reader) 159 { 160 throw new Exception("Can't parse %s from %s".format(T.stringof, inputType)); 161 } 162 } 163 164 void handleString(S)(S s) 165 { 166 static if (is(typeof(s.to!T))) 167 { 168 T v = to!T(s); 169 handleValue(v); 170 } 171 else 172 throw new Exception("Can't parse %s from %s".format(T.stringof, S.stringof)); 173 } 174 175 static if (is(T : C[])) 176 void handleStringFragments(Reader)(Reader reader) 177 { 178 static struct FragmentSink 179 { 180 C[] buf; 181 182 void handleStringFragment(CC)(CC[] s) 183 { 184 buf ~= s; 185 } 186 } 187 FragmentSink sink; 188 reader(&sink); 189 handleValue(sink.buf); 190 } 191 else 192 alias handleStringFragments = unparseable!"string fragments"; 193 194 static if (is(T U : U[])) 195 void handleArray(Reader)(Reader reader) 196 { 197 ArraySink!U sink; 198 reader(&sink); 199 handleValue(sink.arr); 200 } 201 else 202 alias handleArray = unparseable!"array"; 203 204 static if (is(T V : V[K], K)) 205 void handleObject(Reader)(Reader reader) 206 { 207 static struct FieldSink 208 { 209 T aa; 210 211 void handleField(NameReader, ValueReader)(NameReader nameReader, ValueReader valueReader) 212 { 213 K k; 214 V v; 215 nameReader (makeSink!K(&k)); 216 valueReader(makeSink!V(&v)); 217 aa[k] = v; 218 } 219 } 220 221 FieldSink sink; 222 reader(&sink); 223 handleValue(sink.aa); 224 } 225 else 226 static if (is(T == struct)) 227 { 228 void handleObject(Reader)(Reader reader) 229 { 230 static struct FieldSink 231 { 232 T s; 233 234 void handleField(NameReader, ValueReader)(NameReader nameReader, ValueReader valueReader) 235 { 236 alias N = const(C)[]; 237 N name; 238 nameReader(makeSink!N(&name)); 239 240 // TODO: generate switch 241 foreach (i, field; s.tupleof) 242 { 243 // TODO: Name customization UDAs 244 enum fieldName = to!N(__traits(identifier, s.tupleof[i])); 245 if (name == fieldName) 246 { 247 alias V = typeof(field); 248 valueReader(makeSink!V(&s.tupleof[i])); 249 return; 250 } 251 } 252 throw new Exception("Unknown field %s".format(name)); 253 } 254 } 255 256 FieldSink sink; 257 reader(&sink); 258 handleValue(sink.s); 259 } 260 } 261 else 262 alias handleObject = unparseable!"object"; 263 264 void handleNull() 265 { 266 static if (is(typeof({T v = null;}))) 267 { 268 T v = null; 269 handleValue(v); 270 } 271 else 272 throw new Exception("Can't parse %s from %s".format(T.stringof, "null")); 273 } 274 275 void handleBoolean(bool v) 276 { 277 static if (is(T : bool)) 278 handleValue(v); 279 else 280 throw new Exception("Can't parse %s from %s".format(T.stringof, "boolean")); 281 } 282 283 void handleNumeric(CC)(CC[] v) 284 { 285 static if (is(typeof(to!T(v)))) 286 { 287 T t = to!T(v); 288 handleValue(t); 289 } 290 else 291 throw new Exception("Can't parse %s from %s".format(T.stringof, "numeric")); 292 } 293 } 294 295 static struct ArraySink(T) 296 { 297 T[] arr; 298 299 void handleValue(ref T v) { arr ~= v; } 300 301 mixin SinkHandlers!T; 302 } 303 304 static auto makeSink(T)(T* p) 305 { 306 static if (is(typeof(p.isSerializationSink))) 307 return p; 308 else 309 { 310 static struct Sink 311 { 312 T* p; 313 314 // TODO: avoid redundant copying for large types 315 void handleValue(ref T v) { *p = v; } 316 317 auto traverse(CC, Reader)(CC[] name, Reader reader) 318 { 319 static if (is(T K : V[K], V)) 320 { 321 auto key = name.to!K(); 322 auto pv = key in *p; 323 if (!pv) 324 { 325 (*p)[key] = V.init; 326 pv = key in *p; 327 } 328 return reader(makeSink(pv)); 329 } 330 else 331 static if (is(T == struct)) 332 { 333 static immutable T dummy; // https://d.puremagic.com/issues/show_bug.cgi?id=12319 334 foreach (i, ref field; p.tupleof) 335 { 336 // TODO: Name customization UDAs 337 enum fieldName = to!(CC[])(__traits(identifier, dummy.tupleof[i])); 338 if (name == fieldName) 339 return reader(makeSink(&field)); 340 } 341 throw new Exception("No such field in %s: %s".format(T.stringof, name)); 342 } 343 else 344 { 345 if (false) // coerce return value 346 return reader(this); 347 else 348 throw new Exception("Can't traverse %s".format(T.stringof)); 349 } 350 } 351 352 mixin SinkHandlers!T; 353 } 354 355 return Sink(p); 356 } 357 } 358 } 359 360 alias Deserializer!Object.makeSink deserializer;