Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/Swim/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public enum Node: Hashable {

// The `Node`'s children.
case fragment([Node])

indirect case preference(PreferenceDict, Node)

// Indicates that no whitespace should be added between the surrounding
// nodes.
Expand Down Expand Up @@ -108,6 +110,8 @@ extension Node: TextOutputStreamable {
}
case .trim:
break
case let .preference(_, child):
child.write(to: &target, depth: &depth, didVisitTrim: &didVisitTrim)
}
}
}
Expand Down
76 changes: 76 additions & 0 deletions Sources/Swim/Preferences.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// File.swift
//
//
// Created by Chris Eidhof on 27.06.21.
//

import Foundation

public struct PreferenceDict: Hashable {
fileprivate var dict: [ObjectIdentifier:AnyHashable] = [:]
}

extension PreferenceDict: Equatable {
public static func ==(lhs: PreferenceDict, rhs: PreferenceDict) -> Bool {
return false
}
}

public protocol PreferenceKey {
associatedtype Value: Hashable
static var defaultValue: Value { get }
static func reduce(value: inout Value, nextValue: () -> Value)
}

extension Node {
public func preference<Key: PreferenceKey>(key: Key.Type = Key.self, _ value: Key.Value) -> Node {
let dict = [ObjectIdentifier(Key.self): value]
return Node.preference(PreferenceDict(dict: dict), self)
}
public func readPreference<Key: PreferenceKey>(key: Key.Type = Key.self) -> Key.Value {
ReadPreferenceVisitor<Key>().visitNode(self)
}
}

struct ReadPreferenceVisitor<Key: PreferenceKey>: Visitor {
func visitElement(name: String, attributes: [String : String], child: Node?) -> Key.Value {
child.map { visitNode($0) } ?? Key.defaultValue
}

func visitText(text: String) -> Key.Value {
Key.defaultValue
}

func visitRaw(raw: String) -> Key.Value {
Key.defaultValue
}

func visitComment(text: String) -> Key.Value {
Key.defaultValue
}

func visitDocumentType(name: String) -> Key.Value {
Key.defaultValue
}

func visitFragment(children: [Node]) -> Key.Value {
guard let f = children.first else { return Key.defaultValue }
var result = visitNode(f)
for other in children.dropFirst() {
Key.reduce(value: &result, nextValue: { visitNode(other) })
}
return result
}

func visitTrim() -> Key.Value {
Key.defaultValue
}

typealias Result = Key.Value
let key = ObjectIdentifier(Key.self)

func visitPreference(_ dict: PreferenceDict, child: Node) -> Key.Value {
(dict.dict[key] as? Key.Value) ?? Key.defaultValue
}
}
8 changes: 8 additions & 0 deletions Sources/Swim/Visitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public protocol Visitor {
func visitNode(_ node: Node) -> Result

func visitTrim() -> Result

func visitPreference(_ dict: PreferenceDict, child: Node) -> Result
}

extension Visitor {
Expand All @@ -37,6 +39,8 @@ extension Visitor {
return visitFragment(children: children)
case .trim:
return visitTrim()
case .preference(let p, let child):
return visitPreference(p, child: child)
}
}
}
Expand Down Expand Up @@ -69,6 +73,10 @@ public extension Visitor where Result == Node {
func visitTrim() -> Result {
.trim
}

func visitPreference(_ p: PreferenceDict, child: Node) -> Result {
.preference(p, child)
}
}

public extension Visitor where Result == Void {
Expand Down
4 changes: 4 additions & 0 deletions Tests/HTMLTests/HTMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ final class HTMLTests: XCTestCase {
func visitTrim() -> [String] {
[]
}

func visitPreference(_ dict: PreferenceDict, child: Node) -> [String] {
visitNode(child)
}
}

let root = body {
Expand Down
55 changes: 55 additions & 0 deletions Tests/SwimTests/PreferenceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// File.swift
//
//
// Created by Chris Eidhof on 27.06.21.
//

import Foundation
import XCTest
import Swim
import HTML

final class PreferenceTests: XCTestCase {
func testSimplePreference() {
struct UseJavascript: PreferenceKey {
static var defaultValue = false
static func reduce(value: inout Bool, nextValue: () -> Bool) {
value = value || nextValue()
}
}

let none: Node = Node.text("Hello")
XCTAssertEqual(none.readPreference(key: UseJavascript.self), false)


let n: Node = div {
"Hello"
"Test".asNode().preference(key: UseJavascript.self, true)
}
let o = n.readPreference(key: UseJavascript.self)
XCTAssertEqual(o, true)
}

func testReduce() {
struct Counter: PreferenceKey {
static var defaultValue = 0
static func reduce(value: inout Int, nextValue: () -> Int) {
value += nextValue()
}
}

let none: Node = Node.text("Hello")
XCTAssertEqual(none.readPreference(key: Counter.self), 0)


let n: Node = div {
"Hello"
"Test".asNode().preference(key: Counter.self, 1)
div {
"Hi".asNode().preference(key: Counter.self, 1)
}
}
XCTAssertEqual(n.readPreference(key: Counter.self), 2)
}
}