Last active
July 22, 2020 15:56
-
-
Save SteveTrewick/e982397a82e3deb2a7a7ed47ef98573d to your computer and use it in GitHub Desktop.
Second and much nicer cut on HTML DSL in swift. No attributes etc yet, but this is a cleaner base design (in some ways at least)
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 | |
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 { | |
static func buildBlock( _ content: String) -> XMLNode { | |
XMLNode(kind: .element).string(content) | |
} | |
static func buildBlock(_ content: XMLNode...) -> XMLNode { | |
XMLNode(kind: .element).children(content.map{$0}) | |
} | |
static func buildBlock(_ content: String, _ children: XMLNode...) -> XMLNode { | |
XMLNode(kind: .element).string(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" } | |
} | |
} | |
} | |
) | |
/* | |
<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> | |
</body> | |
</html> | |
*/ |
Added in some css attributes. Note we can lose the underscore and use variadic notation for the first param while avoiding the need to actually use the name of the trailing closure param. Fun!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated to clean up a bit