1 /*
2 Building XML with RAII techniques.
3 */
4 module mage.util.xml;
5 import mage;
6 import mage.util.stream;
7 import mage.util.mem;
8 
9 import std.format : format;
10 
11 struct Text
12 {
13   Element* parent;
14   string content;
15 }
16 
17 struct Attribute
18 {
19   string key;
20   string value;
21 }
22 
23 struct Element
24 {
25   Doc* doc;
26   Element* parent;
27   string name;
28   Element*[] children;
29   Attribute*[] attributes;
30 
31   Element* child(string name)
32   {
33     auto c = doc.mem.allocate!Element(doc, &this, name);
34     children ~= c;
35     return c;
36   }
37 
38   Attribute* attr(in string key, in string value)
39   {
40     foreach(a; attributes) {
41       if(a.key == key) {
42         a.value = value;
43         return a;
44       }
45     }
46     auto a = doc.mem.allocate!Attribute(key, value);
47     attributes ~= a;
48     return a;
49   }
50 
51   Text* text(in string content)
52   {
53     return null;
54   }
55 }
56 
57 struct Doc
58 {
59   Block!(4.KiB) mem;
60 
61   string xmlVersion = "1.0";
62   string xmlEncoding = "UTF-8";
63   Element*[] children;
64 
65   Element* child(string name) {
66     auto n = mem.allocate!Element(&this, null, name);
67     children ~= n;
68     return n;
69   }
70 }
71 
72 
73 unittest {
74   Doc doc;
75   with(doc) {
76     with(child("Project")) {
77       attr("DefaultTargets", "Build");
78       attr("ToolsVersion", "12.0");
79       attr("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003");
80       with(child("ItemGroup")) {
81         attr("Label", "ProjectConfigurations");
82         with(child("ProjectConfiguration")) {
83           attr("Include", "Debug|Win32");
84           with(child("Configuration")) {
85             text("Debug");
86           }
87           with(child("Platform")) {
88             text("Win32");
89           }
90         }
91       }
92     }
93   }
94 }
95 
96 void serialize(S)(ref S stream, ref Doc doc)
97 {
98   stream.write(`<?xml version="%s" encoding="%s"?>`.format(doc.xmlVersion, doc.xmlEncoding));
99   foreach(c; doc.children) {
100     stream.serialize(*c);
101   }
102   stream.writeln();
103 }
104 
105 void serialize(S)(ref S stream, ref Element n)
106 {
107   import std.stdio : stdout = write;
108   
109   stream.writeln();
110   stream.write("<%s".format(n.name));
111   foreach(a; n.attributes) {
112     stream.write(" ");
113     stream.serialize(*a);
114   }
115   if(n.children.length > 0)
116   {
117     stream.write(">");
118     stream.indent();
119     foreach(c; n.children) {
120       stream.serialize(*c);
121     }
122     stream.dedent();
123     stream.writeln();
124     stream.write("<%s/>".format(n.name));
125   }
126   else {
127     stream.write("/>");
128   }
129 }
130 
131 void serialize(S)(ref S stream, ref Attribute a)
132 {
133   stream.write(`%s="%s"`.format(a.key, a.value));
134 }
135 
136 struct StdoutStream
137 {
138   mixin StreamWriteln;
139 
140   void write(string s)
141   {
142     import std.stdio : write;
143     write(s);
144   }
145 }
146 
147 unittest {
148   Doc doc;
149   with(doc) {
150     foreach(i; 0..3)
151     {
152       with(child("A")) {
153         attr("id", "a%s".format(i));
154         attr("name", "the-a");
155         with(child("B")) {
156           attr("name", "the-b");
157           with(child("C1")) {
158             attr("name", "the-c");
159           }
160           with(child("C2")) {
161             attr("name", "the-other-c");
162           }
163         }
164       }
165     }
166   }
167   //StringStream s;
168   //s.writeln();
169   StdoutStream l;
170   //l.newLine = "\\n\n";
171   //l.indentString = "=>";
172   l.serialize(doc);
173   //log(s.content);
174 }