Last active
February 25, 2021 10:46
-
-
Save SteveTrewick/58404639a230dab5020c28ede763bb32 to your computer and use it in GitHub Desktop.
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 | |
@_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"} | |
} | |
} | |
} | |
} | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sources of info that were super useful
https://www.andyibanez.com/posts/understanding-function-builders/
https://medium.com/@carson.katri/create-your-first-function-builder-in-5-minutes-b4a717390671
https://vihan.org/blog/swift-function-builders/
https://dev.to/gualtierofr/introduction-to-function-builders-4c1