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