Skip to content

Commit

Permalink
String Interpolation, Equatability, Hashability (#48)
Browse files Browse the repository at this point in the history
* String Interpolation, Equatability, Hashability

* More

* Add default for String
  • Loading branch information
stephencelis authored Mar 25, 2019
1 parent 227aa3e commit ac09f71
Showing 1 changed file with 125 additions and 6 deletions.
131 changes: 125 additions & 6 deletions Sources/Html/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,39 @@ public enum Node {
}

extension Node {
/// Default HTML DOCTYPE.
public static let doctype: Node = .doctype("html")

/// A root document node including the default HTML DOCTYPE.
public static func document(_ children: Node...) -> Node {
return .fragment([doctype] + children)
}

public mutating func append(_ node: Node) {
switch (self, node) {
case (.fragment(var nodes), .fragment(let moreNodes)):
nodes.append(contentsOf: moreNodes)
self = .fragment(nodes)
case (.fragment(var nodes), let node):
nodes.append(node)
self = .fragment(nodes)
case (let node, .fragment(var nodes)):
nodes.insert(node, at: nodes.startIndex)
self = .fragment(nodes)
case (.comment(var comment), .comment(let anotherComment)):
comment.append(contentsOf: anotherComment)
self = .comment(comment)
case (.raw(var raw), .raw(let anotherRaw)):
raw.append(contentsOf: anotherRaw)
self = .raw(raw)
case (.text(var text), .text(let anotherText)):
text.append(contentsOf: anotherText)
self = .text(text)
default:
self = .fragment([self, node])
}
}

public var isEmpty: Bool {
switch self {
case let .comment(string), let .doctype(string), let .raw(string), let .text(string):
Expand All @@ -32,6 +65,64 @@ extension Node {
}
}

extension Node: Equatable {
public static func == (lhs: Node, rhs: Node) -> Bool {
switch (lhs, rhs) {
case
let (.comment(lhs), .comment(rhs)),
let (.doctype(lhs), .doctype(rhs)),
let (.raw(lhs), .raw(rhs)),
let (.text(lhs), .text(rhs)):
return lhs == rhs
case let (
.element(lhsTag, lhsAttributes, lhsChildren),
.element(rhsTag, rhsAttributes, rhsChildren)):

return lhsTag == rhsTag
&& zip(lhsAttributes, rhsAttributes).allSatisfy { $0 == $1 }
&& lhsChildren == rhsChildren
case let (.fragment(lhs), .fragment(rhs)):
return lhs == rhs
default:
return false
}
}
}

extension Node: Hashable {
public func hash(into hasher: inout Hasher) {
enum HashingTag: String {
case comment, doctype, element, fragment, raw, text
}

switch self {
case let .comment(comment):
hasher.combine(HashingTag.comment)
hasher.combine(comment)
case let .doctype(doctype):
hasher.combine(HashingTag.doctype)
hasher.combine(doctype)
case let .element(tag, attributes, children):
hasher.combine(HashingTag.element)
hasher.combine(tag)
attributes.forEach {
hasher.combine($0)
hasher.combine($1)
}
hasher.combine(children)
case let .fragment(nodes):
hasher.combine(HashingTag.fragment)
hasher.combine(nodes)
case let .raw(raw):
hasher.combine(HashingTag.raw)
hasher.combine(raw)
case let .text(text):
hasher.combine(HashingTag.text)
hasher.combine(text)
}
}
}

extension Node: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Node...) {
self = .fragment(elements)
Expand All @@ -44,12 +135,40 @@ extension Node: ExpressibleByStringLiteral {
}
}

extension Node {
/// Default HTML DOCTYPE.
public static let doctype: Node = .doctype("html")
#if swift(>=5.0)
public struct NodeStringInterpolation: StringInterpolationProtocol {
var node: Node

/// A root document node including the default HTML DOCTYPE.
public static func document(_ children: Node...) -> Node {
return .fragment([doctype] + children)
public init(literalCapacity: Int, interpolationCount: Int) {
self.node = .fragment([])
}

public mutating func appendInterpolation(_ value: Node) {
self.node.append(value)
}

public mutating func appendInterpolation(_ value: String) {
self.node.append(.text(value))
}

public mutating func appendLiteral(_ literal: String) {
self.node.append(.text(literal))
}
}

extension Node: ExpressibleByStringInterpolation {
public init(stringInterpolation: NodeStringInterpolation) {
self = stringInterpolation.node
}
}
#else
extension Node: ExpressibleByStringInterpolation {
public init(stringInterpolation strings: Node...) {
self = .fragment(strings)
}

public init<T>(stringInterpolationSegment expr: T) {
self = .text("\(expr)")
}
}
#endif

0 comments on commit ac09f71

Please sign in to comment.