1 module yamlserialized.deserialization;
2 
3 import std.conv;
4 import std.traits;
5 
6 import dyaml;
7 
8 void deserializeInto(T)(Node yamlNode, ref T array) if (isArray!T) {
9     alias ElementType = ForeachType!T;
10 
11     // Iterate each item in the array of nodes and add them to values, converting them to the actual type
12     foreach(item; yamlNode.as!(Node[])) {
13         static if (is(ElementType == struct)) {
14             // This item is a struct - instantiate it
15             ElementType newStruct;
16 
17             // ...deserialize into the new instance
18             item.deserializeInto(newStruct);
19 
20             // ...and add it to the array
21             array ~= newStruct;
22         }
23         else static if (is(ElementType == class)) {
24             // The item type is class - create a new instance
25             auto newClass = new ElementType();
26 
27             // ...deserialize into the new instance
28             item.deserializeInto(newClass);
29 
30             // ...and add it to the array
31             array ~= newClass;
32         }
33         else static if (isSomeString!ElementType) {
34             array ~= item.as!string.to!ElementType;
35         }
36         else static if (isArray!ElementType) {
37             // An array of arrays. Recursion time!
38             ElementType subArray;
39 
40             item.deserializeInto(subArray);
41             array ~= subArray;
42         }
43         else {
44             array ~= item.as!ElementType;
45         }
46     }
47 }
48 
49 void deserializeInto(T)(Node yamlNode, ref T associativeArray) if (isAssociativeArray!T) {
50     alias VType = ValueType!T;
51 
52     // Iterate each Pair in the Node
53     foreach(pair; yamlNode.as!(Node.Pair[])) {
54         auto key = pair.key.as!string.to!(KeyType!T);
55         auto value = pair.value;
56 
57         static if (isAssociativeArray!VType) {
58             /* The associative array's value type is another associative array type.
59                It's recursion time. */
60 
61             if (key in associativeArray) {
62                 value.deserializeInto(associativeArray[key]);
63             }
64             else {
65                 VType subAssocArray;
66 
67                 value.deserializeInto(subAssocArray);
68                 associativeArray[key] = subAssocArray;
69             }
70         }
71         else static if (is(VType == struct)) {
72             // The value type is a struct - instantiate it
73             VType newStruct;
74 
75             // ...deserialize into the new instance
76             value.deserializeInto(newStruct);
77 
78             // ...and add it to the associative array
79             associativeArray[key] = newStruct;
80         }
81         else static if (is(VType == class)) {
82             // The value type is class - create a new instance
83             auto newClass = new VType();
84 
85             // ...deserialize into the new instance
86             value.deserializeInto(newClass);
87 
88             // ...and add it to the associative array
89             associativeArray[key] = newClass;
90         }
91         else {
92             associativeArray[key] = value.as!VType;
93         }
94     }
95 }
96 
97 void deserializeInto(T)(Node yamlNode, ref T obj) if (is(T == struct) || is(T == class)) {
98     enum fieldNames = FieldNameTuple!T;
99 
100     foreach(fieldName; fieldNames) {
101         alias FieldType = typeof(__traits(getMember, obj, fieldName));
102 
103         if (!yamlNode.containsKey(fieldName)) {
104             continue;
105         }
106 
107         static if (is(FieldType == struct)) {
108             // This field is a struct - recurse into it
109             yamlNode[fieldName].deserializeInto(__traits(getMember, obj, fieldName));
110         }
111         else static if (is(FieldType == class)) {
112             // This field is a class - recurse into it unless it is null
113             if (__traits(getMember, obj, fieldName) !is null) {
114                 yamlNode[fieldName].deserializeInto(__traits(getMember, obj, fieldName));
115             }
116         }
117         else static if (isSomeChar!FieldType) {
118             // Field is a char
119             /* Node.as!char fails for some reason, so we have to retrieve it as a string first
120                and then convert it to the correct type. */
121             __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!string.to!FieldType;
122         }
123         else static if (isSomeString!FieldType) {
124             // Field is a string
125             __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!FieldType;
126         }
127         else static if (isArray!FieldType) {
128             // Field is an array
129             yamlNode[fieldName].deserializeInto(__traits(getMember, obj, fieldName));
130         }
131         else static if (isAssociativeArray!FieldType) {
132             // Field is an associative array
133             yamlNode[fieldName].deserializeInto(__traits(getMember, obj, fieldName));
134         }
135         else static if (isIntegral!FieldType) {
136             // Field is an integer
137             if (yamlNode[fieldName].isInt) {
138                 // If node contains an integer value, get it directly
139                 __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!FieldType;
140             }
141             else {
142                 // If node contains a non-integer value, convert it to a string first and then to the correct type
143                 __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!string.to!FieldType;
144             }
145         }
146         else static if (isBoolean!FieldType) {
147             /* Convert to string first, then to the correct boolean type. */
148             __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!string.to!FieldType;
149         }
150         else {
151             __traits(getMember, obj, fieldName) = yamlNode[fieldName].as!FieldType;
152         }
153     }
154 }
155 
156 T deserializeTo(T)(Node yamlNode) if (is(T == struct)) {
157     T obj;
158 
159     yamlNode.deserializeInto(obj);
160     return obj;
161 }