import Rainbow public protocol NodeModifier { // swiftlint:disable type_name associatedtype _Body: TextNode typealias Body = _Body // swiftlint:enable type_name associatedtype Content: TextNode @TextBuilder func render(content: Content) -> Body } public extension NodeModifier { func concat(_ modifier: T) -> ConcatModifier { print("Concat: \(type(of: modifier))") return .init(firstModifier: self, secondModifier: modifier) } } public struct ConcatModifier: NodeModifier where M1.Content == M0.Body { let firstModifier: M0 let secondModifier: M1 public func render(content: M0.Content) -> some TextNode { let firstOutput = firstModifier.render(content: content) return secondModifier.render(content: firstOutput) } } public struct ModifiedNode { @usableFromInline let content: Content @usableFromInline let modifier: Modifier @usableFromInline init(content: Content, modifier: Modifier) { self.content = content self.modifier = modifier } } extension ModifiedNode: TextNode where Modifier.Content == Content { public func render() -> String { modifier.render(content: content).render() } @inlinable func apply(_ modifier: M) -> ModifiedNode> { print("ModifiedNode modifier called.") return .init(content: content, modifier: self.modifier.concat(modifier)) } } public extension TextNode { @inlinable func modifier(_ modifier: M) -> ModifiedNode { .init(content: self, modifier: modifier) } } @usableFromInline struct ColorModifier: NodeModifier { @usableFromInline let color: NamedColor @usableFromInline init(color: NamedColor) { self.color = color } @usableFromInline func render(content: Content) -> some TextNode { content.render().applyingColor(color) } } public extension TextNode { @inlinable func color(_ color: NamedColor) -> some TextNode { modifier(ColorModifier(color: color)) } } @usableFromInline struct StyleModifier: NodeModifier { @usableFromInline let styles: [Style] @usableFromInline init(styles: [Style]) { self.styles = styles } @usableFromInline func render(content: Content) -> some TextNode { styles.reduce(content.render()) { $0.applyingStyle($1) } } } public extension TextNode { @inlinable func style(_ styles: Style...) -> some TextNode { modifier(StyleModifier(styles: styles)) } @inlinable func style(_ styles: [Style]) -> some TextNode { modifier(StyleModifier(styles: styles)) } }