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;