1 /** 
2  * Deserialization utilities
3  */
4 module jstruct.deserializer;
5 
6 import std.json;
7 import jstruct.exceptions : DeserializationError;
8 import std.traits : FieldTypeTuple, FieldNameTuple, isArray, ForeachType, EnumMembers, fullyQualifiedName;;
9 import std.conv : to;
10 
11 /** 
12  * Deserializes the provided JSON into a struct of type RecordType
13  *
14  * Params:
15  *   jsonIn = the JSON to deserialize
16  * Throws:
17  *   RemoteFieldMissing = if the field names in the provided RecordType
18  *   	      cannot be found within the prpvided JSONValue `jsonIn`.
19  * Returns: an instance of RecordType
20  */
21 public RecordType fromJSON(RecordType)(JSONValue jsonIn)
22 {
23 	RecordType record;
24 
25 	// Alias as to only expand later when used in compile-time
26 	alias structTypes = FieldTypeTuple!(RecordType);
27 	alias structNames = FieldNameTuple!(RecordType);
28 	alias structValues = record.tupleof;
29 
30 	static foreach(cnt; 0..structTypes.length)
31 	{
32 		debug(dbg)
33 		{
34 			pragma(msg, structTypes[cnt]);
35 			pragma(msg, structNames[cnt]);
36 			// pragma(msg, structValues[cnt]);
37 		}
38 
39 		debug(dbg)
40 		{
41 			pragma(msg, "Bruh type");
42 			pragma(msg,  structTypes[cnt]);
43 			// pragma(msg, __traits(identifier, mixin(structTypes[cnt])));
44 		}
45 
46 		try
47 		{
48 			static if(__traits(isSame, structTypes[cnt], byte))
49 			{
50 				mixin("record."~structNames[cnt]) = cast(byte)jsonIn[structNames[cnt]].integer();
51 			}
52 			else static if(__traits(isSame, structTypes[cnt], ubyte))
53 			{
54 				mixin("record."~structNames[cnt]) = cast(ubyte)jsonIn[structNames[cnt]].uinteger();
55 			}
56 			else static if(__traits(isSame, structTypes[cnt], short))
57 			{
58 				mixin("record."~structNames[cnt]) = cast(short)jsonIn[structNames[cnt]].integer();
59 			}
60 			else static if(__traits(isSame, structTypes[cnt], ushort))
61 			{
62 				mixin("record."~structNames[cnt]) = cast(ushort)jsonIn[structNames[cnt]].uinteger();
63 			}
64 			else static if(__traits(isSame, structTypes[cnt], int))
65 			{
66 				mixin("record."~structNames[cnt]) = cast(int)jsonIn[structNames[cnt]].integer();
67 			}
68 			else static if(__traits(isSame, structTypes[cnt], uint))
69 			{
70 				mixin("record."~structNames[cnt]) = cast(uint)jsonIn[structNames[cnt]].uinteger();
71 			}
72 			else static if(__traits(isSame, structTypes[cnt], ulong))
73 			{
74 				mixin("record."~structNames[cnt]) = cast(ulong)jsonIn[structNames[cnt]].uinteger();
75 			}
76 			else static if(__traits(isSame, structTypes[cnt], long))
77 			{
78 				mixin("record."~structNames[cnt]) = cast(long)jsonIn[structNames[cnt]].integer();
79 			}
80 			else static if(__traits(isSame, structTypes[cnt], string))
81 			{
82 				mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].str();
83 
84 				debug(dbg)
85 				{
86 					pragma(msg,"record."~structNames[cnt]);
87 				}
88 			}
89 			else static if(__traits(isSame, structTypes[cnt], JSONValue))
90 			{
91 				mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]];
92 
93 				debug(dbg)
94 				{
95 					pragma(msg,"record."~structNames[cnt]);
96 				}
97 			}
98 			else static if(__traits(isSame, structTypes[cnt], bool))
99 			{
100 				mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean();
101 
102 				debug(dbg)
103 				{
104 					pragma(msg,"record."~structNames[cnt]);
105 				}
106 			}
107 			else static if(isArray!(structTypes[cnt]))
108 			{
109 				alias recordArrayComponent = mixin("record."~structNames[cnt]);
110 
111 				JSONValue[] jsonArray = jsonIn[structNames[cnt]].array();
112 
113 				for(ulong i = 0; i < jsonArray.length; i++)
114 				{
115 					JSONValue jsonVal = jsonArray[i];
116 
117 					static if(__traits(isSame, ForeachType!(structTypes[cnt]), byte))
118 					{
119 						mixin("record."~structNames[cnt])~= cast(byte)jsonVal.integer();
120 					}
121 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ubyte))
122 					{
123 						mixin("record."~structNames[cnt])~= cast(ubyte)jsonVal.uinteger();
124 					}
125 					static if(__traits(isSame, ForeachType!(structTypes[cnt]), short))
126 					{
127 						mixin("record."~structNames[cnt])~= cast(short)jsonVal.integer();
128 					}
129 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ushort))
130 					{
131 						mixin("record."~structNames[cnt])~= cast(ushort)jsonVal.uinteger();
132 					}
133 					static if(__traits(isSame, ForeachType!(structTypes[cnt]), int))
134 					{
135 						mixin("record."~structNames[cnt])~= cast(int)jsonVal.integer();
136 					}
137 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), uint))
138 					{
139 						mixin("record."~structNames[cnt])~= cast(uint)jsonVal.uinteger();
140 					}
141 					static if(__traits(isSame, ForeachType!(structTypes[cnt]), long))
142 					{
143 						mixin("record."~structNames[cnt])~= cast(long)jsonVal.integer();
144 					}
145 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), ulong))
146 					{
147 						mixin("record."~structNames[cnt])~= cast(ulong)jsonVal.uinteger();
148 					}
149 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), bool))
150 					{
151 						mixin("record."~structNames[cnt])~= jsonVal.boolean();
152 					}
153 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), float))
154 					{
155 						mixin("record."~structNames[cnt])~= cast(float)jsonVal.floating();
156 					}
157 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), double))
158 					{
159 						mixin("record."~structNames[cnt])~= cast(double)jsonVal.floating();
160 					}
161 					else static if(__traits(isSame, ForeachType!(structTypes[cnt]), string))
162 					{
163 						mixin("record."~structNames[cnt])~= jsonVal.str();
164 					}
165 					
166 
167 
168 				}
169 
170 
171 				// mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].array();
172 
173 				debug(dbg)
174 				{
175 					pragma(msg,"record."~structNames[cnt]);
176 				}
177 			}
178 			else static if(is(structTypes[cnt] == enum))
179 			{
180 				string enumChoice = jsonIn[structNames[cnt]].str();
181 
182 				alias members = EnumMembers!(structTypes[cnt]);
183 
184 				version(dbg)
185 				{
186 					import std.stdio : writeln;
187 				}
188 				
189 				static foreach(member; members)
190 				{
191 					version(dbg)
192 					{
193 						writeln(member);
194 						writeln(fullyQualifiedName!(member));
195 						writeln(__traits(identifier, member));
196 					}
197 					
198 					if(__traits(identifier, member) == enumChoice)
199 					{
200 						mixin("record."~structNames[cnt]) = member;
201 					}
202 				}
203 			}
204 			else
205 			{
206 				// throw new
207 				//TODO: Throw error
208 				debug(dbg)
209 				{
210 					pragma(msg, "Unknown type for de-serialization");
211 
212 					pragma(msg, is(structTypes[cnt] == enum));
213 
214 
215 				}
216 			}
217 		}
218 		catch(JSONException e)
219 		{
220 			throw new DeserializationError();
221 		}
222 	}
223 
224 	return record;
225 }
226 
227 /**
228  * Example deserialization of JSON
229  * to our `Person` struct
230  */
231 unittest
232 {
233 	enum EnumType
234 	{
235 		DOG,
236 		CAT
237 	}
238 	
239 	struct Person
240 	{
241 		public string firstname, lastname;
242 		public int age;
243 		public bool isMale;
244 		public JSONValue obj;
245 		public int[] list;
246 		public bool[] list2;
247 		public float[] list3;
248 		public double[] list4;
249 		public string[] list5;
250 		public EnumType animal;
251 	}
252 	
253 	JSONValue json = parseJSON(`{
254 "firstname" : "Tristan",
255 "lastname": "Kildaire",
256 "age": 23,
257 "obj" : {"bruh":1},
258 "isMale": true,
259 "list": [1,2,3],
260 "list2": [true, false],
261 "list3": [1.5, 1.4],
262 "list4": [1.5, 1.4],
263 "list5": ["baba", "booey"],
264 "animal": "CAT"
265 }
266 `);
267 
268 	Person person = fromJSON!(Person)(json);
269 
270 	debug(dbg)
271 	{
272 		writeln("Deserialized as: ", person);	
273 	}
274 
275 	assert(cmp(person.firstname, "Tristan") == 0);
276 	assert(cmp(person.lastname, "Kildaire") == 0);
277 	assert(person.age == 23);
278 	assert(person.isMale == true);
279 	assert(person.obj["bruh"].integer() == 1);
280 	assert(person.list == [1,2,3]);
281 	assert(person.list2 == [true, false]);
282 	assert(person.list3 == [1.5F, 1.4F]);
283 	assert(person.list4 == [1.5, 1.4]);
284 	assert(person.list5 == ["baba", "booey"]);
285 	assert(person.animal == EnumType.CAT);
286 
287 }
288 
289 version(unittest)
290 {
291 	import std.string : cmp;
292 	import std.stdio : writeln;
293 }
294 
295 /**
296  * Another example deserialization of JSON
297  * to our `Person` struct but here there
298  * is a problem with deserialization as
299  * there is a missing field `isMale`
300  * in the provided JSON
301  */
302 unittest
303 {
304 	struct Person
305 	{
306 		public string firstname, lastname;
307 		public int age;
308 		public bool isMale;
309 		public JSONValue obj;
310 		public int[] list;
311 		
312 	}
313 	
314 	JSONValue json = parseJSON(`{
315 "firstname" : "Tristan",
316 "lastname": "Kildaire",
317 "age": 23,
318 "obj" : {"bruh":1},
319 "list": [1,2,3]
320 }
321 `);
322 
323 	try
324 	{
325 		Person person = fromJSON!(Person)(json);
326 		assert(false);
327 	}
328 	catch(DeserializationError)
329 	{
330 		assert(true);
331 	}
332 	
333 }
334 
335 
336 unittest
337 {
338 	enum EnumType
339 	{
340 		DOG,
341 		CAT
342 	}
343 
344 	struct Person
345 	{
346 		public string firstname, lastname;
347 
348 		
349 		public EnumType animal;
350 		
351 	}
352 	
353 	JSONValue json = parseJSON(`{
354 "firstname" : "Tristan",
355 "lastname": "Kildaire",
356 "animal" : "CAT"
357 }
358 `);
359 
360 	try
361 	{
362 		Person person = fromJSON!(Person)(json);
363 		writeln(person);
364 		assert(true);
365 	}
366 	catch(DeserializationError)
367 	{
368 		assert(false);
369 	}
370 	
371 }