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 }