1 module keyvalues.deserializer; 2 3 import std.algorithm; 4 import std.conv; 5 import std.range; 6 import std.string; 7 import std.traits; 8 9 import keyvalues.keyvalue; 10 import keyvalues.parser; 11 12 private enum decodable(Layout) = isScalarType!Layout || isSomeString!Layout; 13 private enum deserializable(Layout) = is(Layout == struct) && __traits(isPOD, Layout); 14 15 /++ 16 Attribute for a field of a Layout which may be missing. 17 +/ 18 struct Optional {} 19 20 /++ 21 Deserialize a KeyValues object into the given Layout. 22 23 A Layout is a struct defining the structure of the KeyValues object. 24 A Layout must be a plain old struct (no fancy constructors), 25 contain only basic types, similarly restricted structs, 26 or arrays of the aforementioned types. 27 28 Params: 29 root = the object to deserialize 30 path = for internal use only, used to report the path to missing keys 31 +/ 32 Layout deserializeKeyValues(Layout)(KeyValue root, string path = "root") 33 { 34 static assert(deserializable!Layout, "Cannot deserialize to " ~ Layout.stringof); 35 36 Layout result; 37 38 foreach(fieldName; __traits(allMembers, Layout)) 39 { 40 enum getMember = "__traits(getMember, Layout, fieldName)"; 41 alias FieldType = typeof(mixin(getMember)); 42 43 static if(isCallable!FieldType) 44 continue; 45 else 46 { 47 string serializedName = fieldName //docs use PascalCase for keys 48 .take(1) 49 .map!toUpper 50 .chain(fieldName.drop(1)) 51 .to!string 52 ; 53 string subpath = path ~ "." ~ fieldName; 54 auto subkeys = root[serializedName]; 55 bool required = !hasUDA!(mixin(getMember), Optional); 56 57 if(subkeys.empty) 58 { 59 if(required) 60 throw new Exception("Required key %s not found".format(subpath)); 61 62 continue; 63 } 64 65 static if(is(FieldType == struct)) 66 mixin("result." ~ fieldName) = subkeys 67 .front 68 .deserializeKeyValues!FieldType(subpath) 69 ; 70 else static if(decodable!FieldType) 71 mixin("result." ~ fieldName) = subkeys 72 .front 73 .value 74 .to!FieldType 75 ; 76 else static if(isDynamicArray!FieldType) 77 { 78 alias FieldElementType = ElementType!FieldType; 79 80 static if(is(FieldElementType == struct)) 81 { 82 string subkeysName = FieldElementType.stringof; 83 auto subkeysPath = subpath ~ "." ~ subkeysName; 84 85 mixin("result." ~ fieldName) = subkeys 86 .front[subkeysName] 87 .map!(kv => kv.deserializeKeyValues!FieldElementType(subkeysPath)) 88 .array 89 ; 90 } 91 else if(decodable!FieldElementType) 92 mixin("result." ~ fieldName) = subkeys.front 93 .map!(kv => kv.value.to!FieldElementType) 94 .array 95 ; 96 else 97 static assert(false, "Can't deserialize array of " ~ FieldElementType); 98 } 99 else 100 static assert(false, "Can't deserialize " ~ FieldType.stringof); 101 } 102 } 103 104 return result; 105 } 106 107 unittest 108 { 109 struct Test 110 { 111 int abc; 112 string def; 113 } 114 115 auto parsed = q{ 116 Abc 32 117 Def "abc def" 118 }.parseKeyValues; 119 120 assert(parsed.deserializeKeyValues!Test == Test(32, "abc def")); 121 } 122 123 unittest 124 { 125 struct Repeated 126 { 127 int abc; 128 } 129 130 struct Test 131 { 132 Repeated[] repeats; 133 string def; 134 } 135 136 auto parsed = q{ 137 Repeats 138 { 139 Repeated 140 { 141 Abc 1 142 } 143 Repeated 144 { 145 Abc 2 146 } 147 } 148 Def "abc def" 149 }.parseKeyValues; 150 151 assert(parsed.deserializeKeyValues!Test == Test([Repeated(1), Repeated(2)], "abc def")); 152 }