1 /** 2 * Very simple write-only API for building XML documents. 3 * Abuses operator overloading to allow a very terse syntax. 4 * 5 * License: 6 * This Source Code Form is subject to the terms of 7 * the Mozilla Public License, v. 2.0. If a copy of 8 * the MPL was not distributed with this file, You 9 * can obtain one at http://mozilla.org/MPL/2.0/. 10 * 11 * Authors: 12 * Vladimir Panteleev <vladimir@thecybershadow.net> 13 */ 14 15 module ae.utils.xmlbuild; 16 17 import ae.utils.xmlwriter; 18 19 /// Create an XML node. Entry point. 20 XmlBuildNode newXml() 21 { 22 return new XmlBuildNode(); 23 } 24 25 /// The node type. Avoid using directly. 26 // Can't use struct pointers, because node["attr"] is 27 // interpreted as indexing the pointer. 28 final class XmlBuildNode 29 { 30 /// Create a child node by calling a "method" on the node 31 XmlBuildNode opDispatch(string name)(string[string] attributes = null) 32 { 33 auto result = new XmlBuildNode(); 34 result._xmlbuild_info.tag = name; 35 foreach (key, value; attributes) 36 result._xmlbuild_info.attributes ~= StringPair(key, value); 37 _xmlbuild_info.children ~= result; 38 return result; 39 } 40 41 /// Add attribute by assigning a "field" on the node 42 @property string opDispatch(string name)(string value) 43 { 44 _xmlbuild_info.attributes ~= StringPair(name, value); 45 return value; 46 } 47 48 /// Add attribute via index 49 string opIndexAssign(string value, string name) 50 { 51 _xmlbuild_info.attributes ~= StringPair(name, value); 52 return value; 53 } 54 55 /// Set inner text via assigning a string 56 void opAssign(string text) 57 { 58 _xmlbuild_info.text = text; 59 } 60 61 override string toString() const 62 { 63 XmlWriter writer; 64 writeTo(writer); 65 return writer.output.get(); 66 } 67 68 final void writeTo(XmlWriter)(ref XmlWriter output) const 69 { 70 with (_xmlbuild_info) 71 { 72 output.startTagWithAttributes(tag); 73 foreach (ref attribute; attributes) 74 output.addAttribute(attribute.key, attribute.value); 75 if (!children.length && !text) 76 { 77 output.endAttributesAndTag(); 78 return; 79 } 80 output.endAttributes(); 81 82 foreach (child; children) 83 child.writeTo(output); 84 output.text(text); 85 86 output.endTag(tag); 87 } 88 } 89 90 // Use a unique name, unlikely to occur in an XML file as a field or attribute. 91 private XmlBuildInfo _xmlbuild_info; 92 } 93 94 private: 95 96 struct StringPair { string key, value; } 97 98 struct XmlBuildInfo 99 { 100 string tag, text; 101 StringPair[] attributes; 102 XmlBuildNode[] children; 103 } 104 105 unittest 106 { 107 auto svg = newXml().svg(); 108 svg.xmlns = "http://www.w3.org/2000/svg"; 109 svg["version"] = "1.1"; 110 auto text = svg.text(["x" : "0", "y" : "15", "fill" : "red"]); 111 text = "I love SVG"; 112 113 assert(svg.toString() == `<svg xmlns="http://www.w3.org/2000/svg" version="1.1"><text fill="red" x="0" y="15">I love SVG</text></svg>`); 114 }