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