1 module yamlserialized.serialization;
2 
3 import std.conv;
4 import std.traits;
5 
6 import yamlserialized.types;
7 
8 import dyaml;
9 
10 @safe:
11 
12 /// Convert an array of type T to a D-YAML Node
13 Node toYAMLNode(T)(in ref T array) if (isArray!T) {
14     alias ElementType = ForeachType!T;
15 
16     Node[] nodes;
17 
18     // Iterate each item in the array and add them to the array of nodes
19     foreach(item; array) {
20         static if (is(ElementType == struct)) {
21             // This item is a struct
22             nodes ~= item.toYAMLNode();
23         }
24         else static if (is(ElementType == class)) {
25             // This item is a class - serialize it unless it is null
26             if (item !is null) {
27                 nodes ~= item.toYAMLNode();
28             }
29         }
30         else static if (isSomeString!ElementType) {
31             nodes ~= Node(item.to!string);
32         }
33         else static if (isArray!ElementType) {
34             // An array of arrays. Recursion time!
35             nodes ~= item.toYAMLNode();
36         }
37         else {
38             nodes ~= Node(item);
39         }
40     }
41 
42     return Node(nodes);
43 }
44 
45 /// Convert an associative array of type T to a D-YAML Node
46 Node toYAMLNode(T)(in ref T associativeArray) if (isAssociativeArray!T) {
47     alias KType = KeyType!T;
48     alias VType = ValueType!T;
49 
50     Node[KType] items;
51 
52     // Iterate each item in the associative array
53     foreach(key, value; associativeArray) {
54         // Convert key to the correct type
55         auto typedKey = key.to!KType;
56 
57         static if (is(VType == struct)) {
58             // The value type is struct
59             items[typedKey] = value.toYAMLNode();
60         }
61         else static if (is(VType == class)) {
62             // The value is a class - serialize it unless it is null
63             if (value !is null) {
64                 items[typedKey] = value.toYAMLNode();
65             }
66         }
67         else static if (isAssociativeArray!VType) {
68             /* The associative array's value type is another associative array type.
69                It's recursion time. */
70             items[typedKey] = value.toYAMLNode();
71         }
72         else static if (isSomeString!VType) {
73             items[typedKey] = Node(value.to!string);
74         }
75         else {
76             items[typedKey] = Node(value);
77         }
78     }
79 
80     return Node(items);
81 }
82 
83 /// Convert a struct or class of type T to a D-YAML Node
84 Node toYAMLNode(T)(in ref T obj) if (is(T == struct) || is(T == class)) {
85     enum fieldNames = FieldNameTuple!T;
86 
87     Node[string] nodes;
88 
89     foreach(fieldName; fieldNames) {
90         auto field = __traits(getMember, obj, fieldName);
91 
92         auto yamlFieldName = fieldName;
93 
94         static if (hasUDA!(__traits(getMember, obj, fieldName), YamlField)) {
95             yamlFieldName = getUDAs!(__traits(getMember, obj, fieldName), YamlField)[0].name;
96         }
97 
98         alias FieldType = typeof(field);
99 
100         static if (is(FieldType == struct)) {
101             // This field is a struct - recurse into it
102             nodes[yamlFieldName] = field.toYAMLNode();
103         }
104         else static if (is(FieldType == class)) {
105             // This field is a class - recurse into it unless it is null
106             if (field !is null) {
107                 nodes[yamlFieldName] = field.toYAMLNode();
108             }
109         }
110         else static if (isSomeChar!FieldType || isSomeString!FieldType) {
111             // Because Node only seems to work with string strings (and not char[], etc), convert all string types to string
112             nodes[yamlFieldName] = Node(field.to!string);
113         }
114         else static if (isArray!FieldType) {
115             // Field is an array
116             nodes[yamlFieldName] = field.toYAMLNode();
117         }
118         else static if (isAssociativeArray!FieldType) {
119             // Field is an associative array
120             nodes[yamlFieldName] = field.toYAMLNode();
121         }
122         else {
123             nodes[yamlFieldName] = Node(field.to!FieldType);
124         }
125     }
126 
127     return Node(nodes);
128 }