1 /** 2 * Intermediary, abstract format for serialization. 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.store; 15 16 import std.conv; 17 import std.exception; 18 import std.string; 19 import std.traits; 20 21 import ae.utils.meta; 22 23 /// A discriminated union type which can be used as both a serialization sink and source. 24 /// Similar to std.variant.Variant and std.json.JSONValue. 25 struct SerializedObject(C) 26 { 27 alias S = C[]; 28 29 enum Type 30 { 31 sNone, 32 sNumeric, 33 sString, 34 sStringFragments, 35 sNull, 36 sBoolean, 37 sArray, 38 sObject, 39 } 40 41 Type type; 42 43 union 44 { 45 S sNumeric; 46 S sString; 47 S[] sStringFragments; 48 bool sBoolean; 49 SerializedObject[] sArray; 50 SerializedObject[S] sObject; 51 } 52 53 // *********************************************************************** 54 55 this(T)(T v) 56 if (is(typeof(this = v))) 57 { 58 this = v; 59 } 60 61 void opAssign(T)(T v) 62 if (is(isNumeric!T)) 63 { 64 type = Type.sNumeric; 65 sNumeric = v.numberToString.to!S; 66 } 67 68 void opAssign(T)(T v) 69 if (isSomeString!T) 70 { 71 type = Type.sString; 72 sString = v.to!S; 73 } 74 75 void opAssign(T)(T v) 76 if (is(T == typeof(null))) 77 { 78 assert(v is null); 79 type = Type.sNull; 80 } 81 82 void opAssign(T)(T v) 83 if (is(T == bool)) 84 { 85 type = Type.sBoolean; 86 sBoolean = v; 87 } 88 89 void opAssign(T)(T v) 90 if (is(T U : U[]) && !isSomeString!T) 91 { 92 type = Type.sArray; 93 import std.range, std.array; 94 sArray = v.map!(e => SerializedObject!C(e)).array; 95 } 96 97 void opAssign(T)(T aa) 98 if (is(T K : V[K], V) && isSomeString!K) 99 { 100 type = Type.sObject; 101 sObject = null; 102 foreach (k, ref v; aa) 103 sObject[k.to!S] = SerializedObject!C(v); 104 } 105 106 ref SerializedObject opIndex(size_t i) 107 { 108 enforce(type == Type.sArray, "SerializedObject is %s, not sArray".format(type)); 109 enforce(i < sArray.length, "SerializedObject sArray index %d out of bounds (0..%d)".format(i, sArray.length)); 110 return sArray[i]; 111 } 112 113 ref SerializedObject opIndex(S s) 114 { 115 enforce(type == Type.sObject, "SerializedObject is %s, not sObject".format(type)); 116 return sObject[s]; 117 } 118 119 // *********************************************************************** 120 121 enum isSerializationSink = true; 122 123 void handleNumeric(CC)(CC[] s) 124 { 125 assert(type == Type.sNone); 126 type = Type.sNumeric; 127 sNumeric = s.to!S; 128 } 129 130 void handleString(CC)(CC[] s) 131 { 132 assert(type == Type.sNone); 133 type = Type.sString; 134 sString = s.to!S; 135 } 136 137 void handleStringFragments(Reader)(Reader reader) 138 { 139 static struct StringFragmentSink 140 { 141 S[]* arr; 142 143 void handleStringFragment(CC)(CC[] s) 144 { 145 *arr ~= s.to!S; 146 } 147 } 148 149 assert(type == Type.sNone); 150 type = Type.sStringFragments; 151 reader(StringFragmentSink(&sStringFragments)); 152 } 153 154 void handleNull() 155 { 156 assert(type == Type.sNone); 157 type = Type.sNull; 158 } 159 160 void handleBoolean(bool value) 161 { 162 assert(type == Type.sNone); 163 type = Type.sBoolean; 164 sBoolean = value; 165 } 166 167 void handleArray(Reader)(Reader reader) 168 { 169 static struct ArraySink 170 { 171 SerializedObject[]* arr; 172 173 alias handleStringFragments = opDispatch!"handleStringFragments"; 174 alias handleObject = opDispatch!"handleObject"; 175 176 template opDispatch(string name) 177 { 178 void opDispatch(Args...)(auto ref Args args) 179 { 180 SerializedObject obj; 181 mixin("obj." ~ name ~ "(args);"); 182 *arr ~= obj; 183 } 184 } 185 } 186 187 assert(type == Type.sNone); 188 type = Type.sArray; 189 reader(ArraySink(&sArray)); 190 } 191 192 void handleObject(Reader)(Reader reader) 193 { 194 static struct ObjectSink 195 { 196 SerializedObject[S]* aa; 197 198 void handleField(NameReader, ValueReader)(NameReader nameReader, ValueReader valueReader) 199 { 200 static struct StringSink 201 { 202 S s; 203 void handleString(CC)(CC[] s) 204 { 205 this.s = s.to!S; 206 } 207 208 void handleStringFragments(Reader)(Reader reader) 209 { 210 reader(&this); 211 } 212 213 void handleStringFragment(CC)(CC[] fragment) 214 { 215 s ~= fragment.to!S; 216 } 217 218 void bad() { throw new Exception("String expected"); } 219 220 void handleNumeric(CC)(CC[] s) { bad(); } 221 void handleNull() { bad(); } 222 void handleBoolean(bool value) { bad(); } 223 void handleArray(Reader)(Reader reader) { bad(); } 224 void handleObject(Reader)(Reader reader) { bad(); } 225 } 226 227 StringSink nameSink; 228 nameReader(nameSink); 229 SerializedObject value; 230 valueReader(&value); 231 (*aa)[nameSink.s] = value; 232 } 233 } 234 235 assert(type == Type.sNone); 236 type = Type.sObject; 237 reader(ObjectSink(&sObject)); 238 } 239 240 auto traverse(CC, Reader)(in CC[] name, Reader reader) 241 { 242 if (type == Type.sNone) 243 { 244 type = Type.sObject; 245 sObject = null; 246 } 247 enforce(type == Type.sObject, "Can't traverse %s".format(type)); 248 249 auto pv = name in sObject; 250 if (!pv) 251 { 252 *p[name] = SerializedObject.init; 253 pv = name in *p; 254 } 255 return reader(pv); 256 } 257 258 // *********************************************************************** 259 260 void read(Sink)(Sink sink) 261 { 262 final switch (type) 263 { 264 case Type.sNone: 265 assert(false, "Uninitialized SerializedObject"); 266 case Type.sNumeric: 267 sink.handleNumeric(sNumeric); 268 break; 269 case Type.sString: 270 sink.handleString(sString); 271 break; 272 case Type.sStringFragments: 273 sink.handleStringFragments(boundFunctorOf!readStringFragments(&this)); 274 break; 275 case Type.sNull: 276 sink.handleNull(); 277 break; 278 case Type.sBoolean: 279 sink.handleBoolean(sBoolean); 280 break; 281 case Type.sArray: 282 sink.handleArray(boundFunctorOf!readArray(&this)); 283 break; 284 case Type.sObject: 285 sink.handleObject(boundFunctorOf!readObject(&this)); 286 break; 287 } 288 } 289 290 void readStringFragments(Sink)(Sink sink) 291 { 292 assert(type == Type.sStringFragments); 293 foreach (fragment; sStringFragments) 294 sink.handleStringFragment(fragment); 295 } 296 297 void readArray(Sink)(Sink sink) 298 { 299 assert(type == Type.sArray); 300 foreach (el; sArray) 301 el.read(sink); 302 } 303 304 struct StringReader 305 { 306 S s; 307 this(S s) { this.s = s; } 308 void opCall(Sink)(Sink sink) 309 { 310 sink.handleString(s); 311 } 312 } 313 314 void readObject(Sink)(Sink sink) 315 { 316 assert(type == Type.sObject); 317 foreach (name, ref value; sObject) 318 sink.handleField(StringReader(name), boundFunctorOf!read(&value)); 319 } 320 } 321 322 unittest 323 { 324 SerializedObject!(immutable(char)) s1, s2; 325 s1 = "aoeu"; 326 s1.read(&s2); 327 } 328 329 unittest 330 { 331 import ae.utils.serialization.json; 332 auto s = jsonParse!(SerializedObject!(immutable(char)))(`null`); 333 assert(s.type == s.Type.sNull); 334 }