1 /// Contains an archive for SDLang.
2 module jaster.serialise.sdlang;
3 
4 private
5 {
6     import std.algorithm, std.format, std.exception, std.traits, std.utf;
7     import sdlang;
8     import jaster.serialise.archive;
9 }
10 
11 /// An `Archive` for SDLang.
12 class ArchiveSDL : Archive
13 {
14     private
15     {
16         ArchiveObject _root;
17     }
18 
19     public
20     {
21         void loadFromTag(Tag tag)
22         {
23             this._root = this.tagToObject(tag);
24         }
25     }
26 
27     public override
28     {
29         const(ubyte[]) saveToMemory()
30         {
31             return cast(const(ubyte[]))this.objectToTag(this.root).toSDLDocument();
32         }
33 
34         void loadFromMemory(const ubyte[] data)
35         {
36             auto text = cast(string)data.idup;
37             validate(text);
38 
39             auto tag = parseSource(text);
40             this._root = this.tagToObject(tag);
41         }
42 
43         @property
44         ArchiveObject root()
45         {
46             if(this._root is null)
47                 this._root = new ArchiveObject();
48 
49             return this._root;
50         }
51     }
52 
53     // ###########
54     // # UTILITY #
55     // ###########
56     private
57     {
58         ArchiveObject tagToObject(Tag tag)
59         {
60             auto object = new ArchiveObject(tag.getFullName().toString);
61 
62             foreach(value; tag.values)
63                 object.addValues(this.sdlToArchive(value));
64 
65             foreach(attrib; tag.attributes)
66                 object.setAttribute(attrib.name, this.sdlToArchive(attrib.value)[0]);
67 
68             foreach(child; tag.all.tags)
69                 object.addChild(this.tagToObject(child));
70 
71             return object;
72         }
73 
74         ArchiveValue[] sdlToArchive(Value value)
75         {
76             ArchiveValue[] data;
77 
78             static foreach(type; Value.AllowedTypes)
79             {
80                 if(value.type == typeid(type))
81                 {
82                     static if(
83                               (isNumeric!type && !is(type == real)) 
84                            || is(type == ubyte[]) 
85                            || is(type == bool) 
86                            || is(type == typeof(null)) 
87                            || is(type == string))
88                         data ~= ArchiveValue(value.get!type);
89                     else static if(is(type == Value[]))
90                     {
91                         foreach(v; value.get!(Value[]))
92                             data ~= sdlToArchive(v);
93                     }
94                     else enforce(false, "Unsupported type: " ~ type.stringof);
95                 }
96             }
97 
98             return data;
99         }
100 
101         Tag objectToTag(ArchiveObject object)
102         {
103             Tag tag = new Tag();
104             tag.name = object.name;
105 
106             foreach(value; object.values)
107                 tag.add(this.archiveToSdl(value));
108 
109             foreach(attrib; object.attributes)
110             {
111                 auto values = this.archiveToSdl(attrib.value);
112                 
113                 enforce(values.length == 1, "Attributes cannot be arrays.");
114                 tag.add(new Attribute(attrib.name, values[0]));
115             }
116 
117             foreach(child; object.children)
118                 tag.add(this.objectToTag(child));
119 
120             return tag;
121         }
122 
123         Value[] archiveToSdl(ArchiveValue value)
124         {
125             Value[] data;
126 
127             static foreach(type; ArchiveValue.AllowedTypes)
128             {
129                 if(value.type == typeid(type))
130                 {
131                     static if(isNumeric!type && !isFloatingPoint!type)
132                         data ~= Value(value.coerce!long);
133                     else static if(isNumeric!type && isFloatingPoint!type)
134                         data ~= Value(value.coerce!double);
135                     else static if(is(type == ubyte[]) || is(type == bool) || is(type == typeof(null)) || is(type == string))
136                         data ~= Value(value.get!type);
137                     else static if(is(type == ArchiveValue[]))
138                     {
139                         foreach(v; value.get!(ArchiveValue[]))
140                             data ~= archiveToSdl(v);
141                     }
142                     else static assert(false, "Unsupported type: " ~ type.stringof);
143                 }
144             }
145 
146             return data;
147         }
148     }
149 }