1 
2 //          Copyright Luna & Cospec 2019.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          https://www.boost.org/LICENSE_1_0.txt)
6 
7 module wsf.serialization.deserializer;
8 import wsf.serialization;
9 import wsf.ast;
10 import std.traits;
11 import std.format;
12 import std.range.primitives;
13 import std.stdio : writeln;
14 
15 private {
16     /*
17         Creates empty class, struct or heap allocated struct
18     */
19     T newEmptyT(T)() {
20         static if (isPointer!T) {
21             static assert(is(PointerTarget!T == struct), "Pointer did not point to a struct!");
22             return new PointerTarget!T;
23         } else static if (is(T == class)) {
24             return new T();
25         } else static if (is(T == struct)) {
26             return *(new T);
27         } else {
28             static assert(0, "Cannot create empty "~T.stringof);
29         }
30     }
31 
32     void deserializeStructOrClass(T)(ref T object, ref Tag tag) {
33         static if (hasStaticMember!(T, "deserialize")) {
34             alias deserializerFunc = __traits(getMember, T, "deserialize");
35             static if (is(Parameters!deserializerFunc[0] : T) && is(Parameters!deserializerFunc[1] == Tag)) {
36                 T.deserialize(object, tag);
37             } else {
38                 static assert(0, "Invalid deserialization function! "~typeof(serializerFunc).stringof);
39             }
40         } else {
41             foreach(memberName; FieldNameTuple!T) {
42 
43                 alias member = __traits(getMember, object, memberName);
44                 enum protection = __traits(getProtection, member);
45                 static if (!hasUDA!(member, ignore)) {
46                     if (memberName !in tag) {
47                         if (hasUDA!(member, optional)) continue;
48                         else throw new Exception("Mandetory field "~memberName~" not present.");
49                     }
50 
51                     // Handle sequences
52                     static if (memberName == "seq") {
53                         static if (is(member : Tag[])) {
54                             __traits(getMember, object, memberName) = tag[memberName];
55                         } else {
56                             if ("seq_" !in tag) throw new Exception("A seq_ tag could not be found!");
57                             deserializeArray!(typeof(member))(__traits(getMember, object, memberName), tag["seq_"]);
58                         }
59                     } else {
60                         deserializeMember!(typeof(member))(__traits(getMember, object, memberName), tag[memberName]);
61                     }
62                 }
63             }
64         }
65     }
66 
67     void deserializeAA(T)(ref T object, ref Tag tag) {
68         foreach(string key; tag.compound.keys) {
69 
70             // Sequential keys cannot be fetched from simple associative arrays
71             if (key == "seq") continue;
72             if (key !in tag) continue;
73             if (tag.length == 0) continue;
74             object[key] = ValueType!T.init;
75             deserializeMember!(ValueType!T)(object[key], tag[key]);   
76         }
77     }
78 
79     void deserializeArray(T)(ref T object, ref Tag tag) {
80         static if (isDynamicArray!T) object.length = tag.length;
81         foreach(i; 0..object.length) {
82             if (tag is null) continue;
83             if (tag.length == 0) continue;
84             if (i > tag.length) return;
85             deserializeMember(object[i], tag[i]);
86         }
87     }
88 
89     void deserializeMember(T)(ref T object, ref Tag tag) {
90         static if (is(T == class) || is(T == struct) || isPointer!T) {
91             deserializeStructOrClass(object, tag);
92         } else static if (isAssociativeArray!T && is(KeyType!T : string) && !is(object : Tag[string])) {
93             deserializeAA(object, tag);
94         } else static if (isArray!T && !is(T : string)) {
95             deserializeArray(object, tag);
96         } else static if (is(object : Tag)) {
97             object = tag;
98         } else static if (is(object : Tag[])) {
99             object = tag.array;
100         } else static if (is(object : Tag[string])) {
101             object = tag.compound;
102         } else {
103             object = tag.get!T;
104         }
105     }
106 }
107 
108 /**
109     Deserialize from a WSF tag
110 */
111 T deserializeWSF(T)(Tag tag) {
112     T val = newEmptyT!T;
113     deserializeMember!T(val, tag);
114     return val;
115 }