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