public protocol NodeModifier { associatedtype Body: Node // swiftlint:disable type_name associatedtype _Content: Node typealias Content = _Content // swiftlint:enable type_name func render(content: Content) -> Body } public extension NodeModifier { func concat( _ modifier: T ) -> ConcatModifier { ConcatModifier(first: self, second: modifier) } } public extension Node { func modifier(_ modifier: T) -> ModifiedNode { .init(content: self, modifier: modifier) } } public struct ConcatModifier: NodeModifier where Content: Node, Modifier1: NodeModifier, Modifier2: NodeModifier, Modifier1.Content == Content, Modifier1.Body == Modifier2.Content { let first: Modifier1 let second: Modifier2 public func render(content: Content) -> some Node { second.render(content: first.render(content: content)) } } public struct ModifiedNode { var content: Content var modifier: Modifier } extension ModifiedNode: NodeRepresentable where Content: NodeRepresentable, Modifier: NodeModifier, Modifier.Content == Content { public func render() -> String { modifier.render(content: content).render() } } extension ModifiedNode: Node where Content: Node, Modifier: NodeModifier, Modifier.Content == Content { public var body: Content { content } } extension ModifiedNode: NodeModifier where Content: NodeModifier, Modifier: NodeModifier, Content.Body == Modifier.Content, Content: Node { public func render(content: Content) -> some Node { let body = content.body return modifier.render(content: body).render() } }