1 /**
2  * Serialization from a D variable.
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.sd.serialization.serializer;
15 
16 import std.traits;
17 
18 import ae.utils.text : fpAsString;
19 import ae.utils.text.ascii : decimalSize, toDec;
20 
21 /// Serialization source which serializes a given object.
22 struct Serializer(T)
23 {
24 	T* object;
25 
26 	auto read(Handler)(Handler handler)
27 	{
28 		static if (__traits(hasMember, Handler, q{canHandleValue})
29 			&& Handler.canHandleValue!(typeof(null))
30 			&& is(typeof(*object is null)))
31 		{
32 			if (*object is null)
33 				return handler.handleValue(null);
34 		}
35 
36 		static if (__traits(hasMember, Handler, q{canHandleValue}) && Handler.canHandleValue!T)
37 			return handler.handleValue(*object);
38 		else
39 		static if (__traits(hasMember, Handler, q{canHandleTypeHint}) && Handler.canHandleTypeHint!T)
40 			return handler.handleTypeHint!T(this);
41 		else
42 		static if (isNumeric!T && __traits(hasMember, Handler, q{handleNumeric}))
43 			return handler.handleNumeric(NumericReader(object));
44 		else
45 		static if (is(T == struct))
46 			return handler.handleMap(StructReader(object));
47 		else
48 		static if (is(T V : V[K], K))
49 			return handler.handleMap(AAReader(object));
50 		else
51 		static if (is(T U : U[]))
52 			return handler.handleArray(ArrayReader(object));
53 		else
54 			static assert(false, "Sink handler " ~ Handler.stringof ~ " can't accept values of type " ~ T.stringof);
55 	}
56 
57 	static if (isIntegral!T)
58 	struct NumericReader
59 	{
60 		T* object;
61 
62 		auto read(Handler)(Handler handler)
63 		{
64 			char[decimalSize!T] buf = void;
65 			return handler.handleSlice!char(toDec(*object, buf));
66 		}
67 	}
68 
69 	static if (isFloatingPoint!T)
70 	struct NumericReader
71 	{
72 		T* object;
73 
74 		auto read(Handler)(Handler handler)
75 		{
76 			auto s = fpAsString(*object);
77 			return handler.handleSlice!char(s.buf.data);
78 		}
79 	}
80 
81 	struct ArrayReader
82 	{
83 		T* object;
84 
85 		auto read(Handler)(Handler handler)
86 		{
87 			alias E = typeof((*object)[0]);
88 
89 			static if (__traits(hasMember, Handler, q{canHandleSlice}) && Handler.canHandleSlice!T)
90 				handler.handleSlice(*object);
91 			else
92 				foreach (ref c; *object)
93 					handler.handleElement(Serializer!E(&c));
94 			return handler.handleEnd();
95 		}
96 	}
97 
98 	struct AAReader
99 	{
100 		T* object;
101 
102 		auto read(Handler)(Handler handler)
103 		{
104 			alias K = typeof((*object).keys[0]);
105 			alias V = typeof((*object).values[0]);
106 			alias Pair = typeof((*object).byKeyValue.front);
107 
108 			struct PairReader
109 			{
110 				Pair* pair;
111 
112 				auto read(Handler)(Handler handler)
113 				{
114 					handler.handlePairKey(Serializer!K(&pair.key()));
115 					handler.handlePairValue(Serializer!V(&pair.value()));
116 					return handler.handleEnd();
117 				}
118 			}
119 
120 			foreach (ref pair; object.byKeyValue())
121 				handler.handlePair(PairReader(&pair));
122 			return handler.handleEnd();
123 		}
124 	}
125 
126 	struct StructReader
127 	{
128 		T* object;
129 
130 		auto read(Handler)(Handler handler)
131 		{
132 			struct FieldReader(size_t fieldIndex)
133 			{
134 				T* object;
135 
136 				auto read(Handler)(Handler handler)
137 				{
138 					static immutable name = __traits(identifier, T.tupleof[fieldIndex]);
139 					handler.handlePairKey(Serializer!(immutable(string))(&name));
140 
141 					alias F = typeof(object.tupleof[fieldIndex]);
142 					handler.handlePairValue(Serializer!F(&object.tupleof[fieldIndex]));
143 
144 					return handler.handleEnd();
145 				}
146 			}
147 
148 			static foreach (fieldIndex; 0 .. T.tupleof.length)
149 				handler.handlePair(FieldReader!fieldIndex(object));
150 			return handler.handleEnd();
151 		}
152 	}
153 
154 	static template Impl(alias anchor)
155 	{
156 		static void read(Sink, T)(Sink sink, auto ref T v)
157 		{
158 			static if (is(typeof(v is null)))
159 				if (v is null)
160 				{
161 					sink.handleNull();
162 					return;
163 				}
164 
165 			static if (is(T == bool))
166 				sink.handleBoolean(v);
167 			else
168 			static if (is(T : ulong))
169 			{
170 				char[decimalSize!T] buf = void;
171 				sink.handleNumeric(toDec(v, buf));
172 			}
173 			else
174 			static if (isNumeric!T) // floating point
175 			{
176 				import ae.utils.textout;
177 
178 				static char[64] arr;
179 				auto buf = StringBuffer(arr);
180 				formattedWrite(&buf, "%s", v);
181 				sink.handleNumeric(buf.get());
182 			}
183 			else
184 			static if (is(T == struct))
185 			{
186 				auto reader = StructReader!T(v.reference);
187 				sink.handleObject(boundFunctorOf!(StructReader!T.read)(&reader));
188 			}
189 			else
190 			static if (is(T V : V[K], K))
191 			{
192 				alias Reader = AAReader!(T, K, V);
193 				auto reader = Reader(v);
194 				sink.handleObject(boundFunctorOf!(Reader.read)(&reader));
195 			}
196 			else
197 			static if (is(T : string))
198 				sink.handleString(v);
199 			else
200 			static if (is(T U : U[]))
201 			{
202 				alias Reader = ArrayReader!T;
203 				auto reader = Reader(v);
204 				sink.handleArray(boundFunctorOf!(Reader.readArray)(&reader));
205 			}
206 			else
207 				static assert(false, "Don't know how to serialize " ~ T.stringof);
208 		}
209 
210 		static struct StructReader(T)
211 		{
212 			RefType!T p;
213 			void read(Sink)(Sink sink)
214 			{
215 				foreach (i, ref field; p.dereference.tupleof)
216 				{
217 					import std.array : split;
218 					enum name = p.dereference.tupleof[i].stringof.split(".")[$-1];
219 
220 					alias ValueReader = Reader!(typeof(field));
221 					auto reader = ValueReader(&field);
222 					sink.handleField(unboundFunctorOf!(stringReader!name), boundFunctorOf!(ValueReader.readValue)(&reader));
223 				}
224 			}
225 		}
226 
227 		static struct AAReader(T, K, V)
228 		{
229 			T aa;
230 			void read(Sink)(Sink sink)
231 			{
232 				foreach (K k, ref V v; aa)
233 				{
234 					alias KeyReader   = Reader!K;
235 					auto keyReader   = KeyReader  (&k);
236 					alias ValueReader = Reader!V;
237 					auto valueReader = ValueReader(&v);
238 					sink.handleField(
239 						boundFunctorOf!(KeyReader  .readValue)(&keyReader  ),
240 						boundFunctorOf!(ValueReader.readValue)(&valueReader),
241 					);
242 				}
243 			}
244 		}
245 
246 		static struct ArrayReader(T)
247 		{
248 			T arr;
249 			void readArray(Sink)(Sink sink)
250 			{
251 				foreach (ref v; arr)
252 					read(sink, v);
253 			}
254 		}
255 
256 		static template stringReader(string name)
257 		{
258 			static void stringReader(Sink)(Sink sink)
259 			{
260 				sink.handleString(name);
261 			}
262 		}
263 
264 		static struct Reader(T)
265 		{
266 			T* p;
267 
268 			void readValue(Sink)(Sink sink)
269 			{
270 				read(sink, *p);
271 			}
272 		}
273 	}
274 }
275 
276 Serializer!T serialize(T)(ref T object)
277 {
278 	return Serializer!T(&object);
279 }