Last active
July 22, 2020 18:14
-
-
Save SteveTrewick/6f16a700fad9efea823f8a1c582c1318 to your computer and use it in GitHub Desktop.
Now with some embedded text tags. I'm not sold on the method or the code, but it beats writing a ton of weird combinations of buildBlock. This is one way. There is another!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// so the main limitation of the previous efforts was that we couldn't do e.g. | |
// p { "here is " em { "some" } "emphasis" } | |
// without starting to write crazy amounts of buildBlock functions, so... | |
extension XMLNode { | |
func named(_ name: String) -> XMLNode { | |
self.name = name | |
return self | |
} | |
func string (_ string: String) -> XMLNode { | |
self.stringValue = string | |
return self | |
} | |
func children(_ children: [XMLNode] ) -> XMLNode { | |
_ = children.map((self as! XMLElement).addChild) | |
return self | |
} | |
func attributes( _ attributes: [XMLNode] ) -> XMLNode { | |
_ = attributes.map((self as! XMLElement).addAttribute) | |
return self | |
} | |
} | |
@_functionBuilder struct XMLBuilder { | |
// I mean, we could parse markdown... | |
static func textNode( _ string: String) -> XMLNode { | |
let doc = try! XMLDocument(xmlString: "<span>\(string)</span>") | |
let node = XMLElement() | |
for child in doc.rootElement()?.children ?? [] { | |
child.detach() | |
node.addChild(child) | |
} | |
return node | |
} | |
static func buildBlock( _ content: String) -> XMLNode { | |
textNode(content) | |
} | |
static func buildBlock(_ content: XMLNode...) -> XMLNode { | |
XMLNode(kind: .element).children(content.map{$0}) | |
} | |
static func buildBlock(_ content: String, _ children: XMLNode...) -> XMLNode { | |
textNode(content).children(children.map{$0}) | |
} | |
} | |
enum CssAttribute { | |
case id (String) | |
case `class` (String) | |
func attribute(_ name: String, _ value: String) -> XMLNode { | |
XMLNode.attribute(withName: name, stringValue: value) as! XMLNode | |
} | |
var node: XMLNode { | |
switch self { | |
case .id (let value): return attribute("id", value) | |
case .class(let value): return attribute("class", value) | |
} | |
} | |
} | |
func html (@XMLBuilder _ content: () -> XMLNode) -> XMLElement { | |
content().named("html") as! XMLElement | |
} | |
func body (_ attribs: CssAttribute..., @XMLBuilder content: () -> XMLNode) -> XMLNode { | |
content().named("body").attributes( attribs.map{$0.node} ) | |
} | |
func div (_ attribs: CssAttribute..., @XMLBuilder content: () -> XMLNode) -> XMLNode { | |
content().named("div").attributes( attribs.map{$0.node} ) | |
} | |
func p (_ attribs: CssAttribute..., @XMLBuilder content: () -> XMLNode) -> XMLNode { | |
content().named("p").attributes( attribs.map{$0.node} ) | |
} | |
func pretty(_ element: XMLElement) { | |
let xmldoc = XMLDocument(rootElement: element) | |
xmldoc.documentContentKind = .html | |
print(xmldoc.xmlString(options: [ .nodePrettyPrint, .documentTidyHTML, .nodePreserveWhitespace])) | |
} | |
pretty ( | |
html { | |
body { | |
div ( .id("main"), .class("content") ) { | |
p { "paragraph 1" } | |
p { "paragraph 2" } | |
} | |
div ( .id("secondary"), .class("content") ) { | |
p { "paragraph 3" } | |
p { "paragraph 4" } | |
} | |
div { | |
// now let's add some in line tags... | |
// if we didn't parse these with XMLDoc they'd be escaped | |
// we lose some type safety, but we would if we went with markdown or similar as well | |
p { | |
"It was the <em>best</em> of days, it was the <strong>worst of <em>all</em></strong> of the days" | |
} | |
} | |
} | |
} | |
) | |
/* | |
<html> | |
<body> | |
<div id="main" class="content"> | |
<p>paragraph 1</p> | |
<p>paragraph 2</p> | |
</div> | |
<div id="secondary" class="content"> | |
<p>paragraph 3</p> | |
<p>paragraph 4</p> | |
</div> | |
<div> | |
<p>It was the <em>best</em> | |
of days, it was the <strong>worst of <em>all</em> | |
</strong> | |
of the days</p> | |
</div> | |
</body> | |
</html> | |
XMLDocument has some weird ideas about how this should be formatted /shrug/ | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment