Skip to content

Instantly share code, notes, and snippets.

@SteveTrewick
Last active February 25, 2021 10:46
Show Gist options
  • Save SteveTrewick/58404639a230dab5020c28ede763bb32 to your computer and use it in GitHub Desktop.
Save SteveTrewick/58404639a230dab5020c28ede763bb32 to your computer and use it in GitHub Desktop.
import Foundation
@_functionBuilder struct XMLBuilder {
static func buildBlock(_ content: String) -> String {
return content
}
static func buildBlock(_ content: XMLElement...) -> [XMLElement] {
return content.map{ $0 }
}
static func buildBlock(_ content: String, _ elements: XMLElement...) -> (String, [XMLElement]) {
return (content, elements.map{$0} )
}
}
extension XMLElement {
convenience init(name: String, content: String, children: [XMLElement] = [], attributes: [XMLNode] = []) {
self.init(name: name)
if content != "" { stringValue = content } // yes, really.
children.forEach(addChild)
attributes.forEach(addAttribute)
}
convenience init(name: String, children: [XMLElement] = [], attributes: [XMLNode] = []) {
self.init(name: name)
children.forEach(addChild)
attributes.forEach(addAttribute)
}
}
enum HTMLLang : String {
case en_GB = "en_GB"
}
enum HTMLAttribute {
case `class` (String)
case id (String)
}
func attributes( dict: [String : String] ) -> [XMLNode] {
var nodes = [XMLNode]()
for (k,v) in dict {
let node = XMLNode(kind: .attribute)
node.name = k
node.stringValue = v
nodes.append(node)
}
return nodes
}
func attribute(htmlAttribute: HTMLAttribute) -> (String, String) {
switch htmlAttribute {
case .class(let value): return ("class",value)
case .id (let value): return ("id", value)
}
}
func attributes( htmlAttributes: [HTMLAttribute] ) -> [XMLNode] {
var dict : [String : String] = [:]
for attrib in htmlAttributes {
let (k, v) = attribute(htmlAttribute: attrib)
dict[k] = v
}
return attributes(dict: dict)
}
func html (lang: HTMLLang, @XMLBuilder _ content: () -> String) -> XMLElement {
return XMLElement(name: "html", content: content(), attributes: attributes(dict: ["lang":lang.rawValue]))
}
func html (lang: HTMLLang, @XMLBuilder _ content: () -> [XMLElement] ) -> XMLElement {
return XMLElement(name: "html", children: content(), attributes: attributes(dict: ["lang":lang.rawValue]))
}
func html (lang: HTMLLang, @XMLBuilder _ content: () -> (String, [XMLElement]) ) -> XMLElement {
let (content, elements) = content()
return XMLElement(name: "html", content: content, children: elements, attributes: attributes(dict: ["lang":lang.rawValue]))
}
func body (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> String) -> XMLElement {
return XMLElement(name: "body", content: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func body (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> [XMLElement] ) -> XMLElement {
return XMLElement(name: "body", children: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func body (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> (String, [XMLElement]) ) -> XMLElement {
let (content, elements) = content()
return XMLElement(name: "body", content: content, children: elements, attributes: attributes( htmlAttributes: htmlAttributes))
}
func div ( _ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> String) -> XMLElement {
return XMLElement(name: "div", content: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func div (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> [XMLElement] ) -> XMLElement {
return XMLElement(name: "div", children: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func div (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> (String, [XMLElement]) ) -> XMLElement {
let (content, elements) = content()
return XMLElement(name: "div", content: content, children: elements, attributes: attributes( htmlAttributes: htmlAttributes))
}
func p ( _ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> String) -> XMLElement {
return XMLElement(name: "p", content: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func p (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> [XMLElement] ) -> XMLElement {
return XMLElement(name: "p", children: content(), attributes: attributes( htmlAttributes: htmlAttributes))
}
func p (_ htmlAttributes: [HTMLAttribute] = [], @XMLBuilder _ content: () -> (String, [XMLElement]) ) -> XMLElement {
let (content, elements) = content()
return XMLElement(name: "p", content: content, children: elements, attributes: attributes( htmlAttributes: htmlAttributes))
}
func pretty(_ element: XMLElement) {
let xmldoc = XMLDocument(rootElement: element)
xmldoc.documentContentKind = .html
print(xmldoc.xmlString(options: [ .nodePrettyPrint, .documentTidyHTML, .nodePreserveWhitespace]))
}
pretty (
html( lang: .en_GB ) {
body {
div ([ .class("dismissed") ]) {
p {"para 1"}
p {"para 2"}
}
p {}
}
}
)
pretty (
html( lang: .en_GB ) {
body {
div ([ .id("wella-wella") ]) {
p { "tell me more" }
p { "tell me more" }
div {
p {"did you get very far?"}
}
}
div {
p ([ .class( "wella" ), .id( "wella" ) ]) {
"tell me more tell me more"
}
div {
p {"like did he have a car?"}
div {
p {"a wella wella wella"}
}
}
}
}
}
)