Skip to content

Commit

Permalink
Result builder fixes (#137)
Browse files Browse the repository at this point in the history
* Link elements are valid in HTML body if they use an itemprop attribute that is body-ok

https://html.spec.whatwg.org/multipage/links.html#body-ok

* Update ContentBuilder DSL to support partial blocks

* Add new statement tests

* Restore the comments

* Remove unused methods from the result builder

---------

Co-authored-by: Mattes Mohr <[email protected]>
  • Loading branch information
tonyarnold and mattesmohr authored Jul 8, 2023
1 parent d166ef5 commit 8f253e7
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Sources/HTMLKit/Abstraction/Elements/HeadElements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ extension Style: GlobalAttributes, GlobalEventAttributes, TypeAttribute, MediaAt
/// ```html
/// <link>
/// ```
public struct Link: EmptyNode, HeadElement {
public struct Link: EmptyNode, HeadElement, BodyElement {

internal var name: String { "link" }

Expand Down
88 changes: 53 additions & 35 deletions Sources/HTMLKit/Framework/Builders/ContentBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
/*
Abstract:
The file contains the builder to build up the result from a sequence of elements.
*/
// Abstract:
// The file contains the builder to build up the result from a sequence of elements.

/// The builder builds up a result value from a sequence of elements.
@resultBuilder public class ContentBuilder<T> {
@resultBuilder public enum ContentBuilder<T> {

public typealias Component = [T]

public typealias Expression = T

/// Builds an empty block
///
/// ```swift
/// Tag {
/// }
/// ```
public static func buildBlock() -> [T] {
public static func buildBlock() -> Component {
return []
}

Expand All @@ -24,8 +26,8 @@
/// }
/// }
/// ```
public static func buildBlock(_ component: T) -> [T] {
return [component]
public static func buildPartialBlock(first: Component) -> Component {
return first
}

/// Builds a block with more than one element.
Expand All @@ -38,10 +40,40 @@
/// }
/// }
/// ```
public static func buildBlock(_ components: T...) -> [T] {
return components.compactMap { $0 }
public static func buildPartialBlock(accumulated: Component, next: Component) -> Component {
return accumulated + next
}

/// Builds a block
///
/// ```swift
/// let content = "Content"
///
/// Tag {
/// content
/// }
/// ```
public static func buildExpression(_ element: Expression?) -> Component {

guard let element else {
return []
}
return [element]
}

/// Builds a block
///
/// ```swift
/// let content: [Type]
///
/// Tag {
/// content
/// }
/// ```
public static func buildExpression(_ element: Component) -> Component {
return element
}

/// Builds a block with one element.
///
/// ```swift
Expand All @@ -53,13 +85,12 @@
/// }
/// }
/// ```
public static func buildOptional(_ component: T?) -> [T] {
if let component = component {
return [component]
public static func buildOptional(_ component: Component?) -> Component {

guard let component else {
return []
}

return []
return component
}

/// Builds a block, if the condition is true.
Expand All @@ -72,8 +103,8 @@
/// }
/// }
/// ```
public static func buildEither(first component: T) -> [T] {
return [component]
public static func buildEither(first: Component) -> Component {
return first
}

/// Builds a block, if the condition is false.
Expand All @@ -90,10 +121,10 @@
/// }
/// }
/// ```
public static func buildEither(second component: T) -> [T] {
return [component]
public static func buildEither(second: Component) -> Component {
return second
}

/// Builds blocks by running through a sequence of elements.
///
/// ```swift
Expand All @@ -105,20 +136,7 @@
/// }
/// }
/// ```
public static func buildArray(_ components: [[T]]) -> [T] {
public static func buildArray(_ components: [Component]) -> Component {
return components.flatMap { $0 }
}

/// Builds a block with embeded content.
///
/// ```swift
/// Tag {
/// content
/// Tag {
/// }
/// }
/// ```
public static func buildBlock(_ content: [T], _ components: T...) -> [T] {
return content + components.compactMap { $0 }
}
}
67 changes: 67 additions & 0 deletions Tests/HTMLKitTests/StatementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,71 @@ final class StatementTests: XCTestCase {
"""
)
}

func testOptionalBeforeElement() throws {

let name: String? = "Tony"

let view = TestView {
if let name = name {
Paragraph {
name
}
}
Division {
}
}

XCTAssertEqual(try renderer.render(view: view),
"""
<p>Tony</p>\
<div></div>
"""
)
}

func testOptionalAfterElement() throws {

let name: String? = "Tony"

let view = TestView {
Division {
}
if let name = name {
Paragraph {
name
}
}
}

XCTAssertEqual(try renderer.render(view: view),
"""
<div></div>\
<p>Tony</p>
"""
)
}

func testOptionalWithExpectedResult() throws {

let name: String? = "Tony"

let view = TestView {
Body {
if let name = name {
Paragraph {
name
}
}
}
}

XCTAssertEqual(try renderer.render(view: view),
"""
<body>\
<p>Tony</p>\
</body>
"""
)
}
}

0 comments on commit 8f253e7

Please sign in to comment.