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 }