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.serializer;
8 import wsf.serialization;
9 import wsf.ast;
10 import std.traits;
11 import std.format;
12 import std.range.primitives;
13 
14 private {
15     void serializeClassOrStruct(T)(T data, ref Tag tag) {
16 
17         static if (!is(T == class) && !is(T == struct) && !isPointer!T) {
18             static assert(0, "Input value was not a class, a struct or a pointer to either.");
19         }
20 
21         static if (is(T == class) || isPointer!T) {
22             if (data is null) return;
23         }
24 
25         static if (isPointer!T) {
26             // Handle pointers
27             serializeClassOrStruct!(PointerTarget!T)(*data, tag);
28         } else {
29             
30             //Handle structs or classes
31             static if (__traits(hasMember, T, "serialize")) {
32                     alias serializerFunc = __traits(getMember, data, "serialize");
33                     static if (is(Parameters!serializerFunc[0] == Tag)) {
34                         data.serialize(tag);
35                     } else {
36                         static assert(0, "Invalid serialization function! "~typeof(serializerFunc).stringof);
37                     }
38             } else {
39                 
40                 // Automatic serialization
41                 foreach(memberName; FieldNameTuple!T) {
42                     
43                     // Get member and protection level of that member
44                     alias member = __traits(getMember, data, memberName);
45                     enum protection = __traits(getProtection, member);
46 
47                     // Only allow public members that aren't ignored
48                     static if (!hasUDA!(member, ignore)) {
49                         static if (memberName == "seq") {
50 
51                             // WSF has a seq tag used for sequential data, if our types match up then we can just use the Tag[] array as a frontend to wsf seq
52                             // Otherwise we should move it to a seq_ tag instead.
53                             static if (is(typeof(member) : Tag[])) {
54                                 pragma(msg, "seq tag mapped to Tag[] array named 'seq'.");
55                                 tag[memberName] = __traits(getMember, data, memberName);
56                             } else {
57                                 pragma(msg, "WARNING: %s %s collides with WSF seq tag, renaming to seq_".format(typeof(member).stringof, memberName));
58                                 serializeMember!(typeof(member))(__traits(getMember, data, memberName), tag, "seq_");
59                             }
60                         } else {
61                             serializeMember!(typeof(member))(__traits(getMember, data, memberName), tag, memberName);
62                         }
63                     }
64                 }
65             }
66         }
67     }
68 
69     void serializeAA(T)(T array, ref Tag tag) {
70         foreach(name, member; array) {
71             static if (is(typeof(member) == class) || isPointer!(typeof(member)) || is(typeof(member) == string)) {
72                 if (member is null) continue;
73             }
74             serializeMember!(typeof(member))(member, tag, name);
75         }
76     }
77 
78     void serializeArray(T)(T array, ref Tag tag) {
79         static if(isArray!(ElementType!T)){
80             
81             // Handle multidimensional arrays
82             foreach(element; array) {
83                 //Serialize arrays
84                 Tag subTag = Tag.emptyArray();
85                 serializeArray(element, subTag);
86                 tag ~= subTag;
87             }
88 
89         } else {
90 
91             // Handle final array values
92             foreach(element; array) {
93                 static if (is(ElementType!T == class) || isPointer!(ElementType!T) || is(ElementType!T == string)) {
94                     if (element is null) continue;
95                 }
96 
97                 serializeMember!(typeof(element), true)(element, tag);
98 
99             }
100 
101         }
102     }
103 
104     void serializeValue(T)(T data, ref Tag tag) {
105         tag = new Tag(data);
106     }
107 
108     void serializeMember(T, bool array = false)(T data, ref Tag tag, string memberName = "") {
109         
110         static if (isPointer!T) {
111 
112             // Handle pointers
113             serializeMember!(PointerTarget!T, memberName, array)(*data, tag);
114 
115         } else static if (is(T == class) || is(T == struct) || isPointer!T) {
116 
117             // Serialize other class types
118             Tag subTag = Tag.emptyCompound();
119             serializeClassOrStruct(data, subTag);
120             static if (array) {
121                 tag ~= subTag;
122             } else {
123                 tag[memberName] = subTag;
124             }
125 
126         } else static if (isAssociativeArray!T) {
127             Tag subTag = Tag.emptyCompound();
128             serializeAA(data, subTag);
129             static if (array) {
130                 tag ~= subTag;
131             } else {
132                 tag[memberName] = subTag;
133             }
134         } else static if(isArray!T && !is(T : string)) {
135 
136             //Serialize arrays
137             Tag subTag = Tag.emptyArray();
138             serializeArray(data, subTag);
139             static if (array) {
140                 tag ~= subTag;
141             } else {
142                 tag[memberName] = subTag;
143             }
144 
145         } else {
146 
147             // Try to serialize everything else
148             static if (array) {
149 
150                 tag ~= new Tag();
151                 serializeValue(data, tag[tag.length-1]);
152 
153             } else {
154 
155                 tag[memberName] = new Tag();
156                 serializeValue(data, tag[memberName]);
157 
158             }
159 
160         }
161     }
162 }
163 
164 /**
165     Serializes a class or struct
166 
167     You can ignore a field with the @ignore UDA
168     
169     Properties will not be serialized.
170 
171     Create function with `void serialize(ref Tag tag)` signature to do custom serialization
172 */
173 Tag serializeWSF(T)(T data) if (is(T == class) || is(T == struct) || isPointer!T) {
174     Tag tag = new Tag();
175     static if (is(T == class) || is(T == struct) || isPointer!T) {
176         tag = Tag.emptyCompound();
177         serializeClassOrStruct!T(data, tag);
178     } else static if(isAssociativeArray!T) {
179         tag = Tag.emptyCompound();
180         serializeAA!T(data, tag);
181     } else static if (isArray!T && !is(T : string)) {
182         tag = Tag.emptyArray();
183         serializeArray!T(data, tag);
184     } else {
185         serializeValue!T(data, tag);
186     }
187     return tag;
188 }