feat: Working on node builder

This commit is contained in:
2024-12-01 01:41:36 -05:00
parent 56a406b231
commit 55d8888961
14 changed files with 480 additions and 39 deletions

View File

@@ -0,0 +1,27 @@
public struct AnyNode: NodeRepresentable {
@usableFromInline
let node: any NodeRepresentable
@inlinable
public init<N: NodeRepresentable>(@NodeBuilder _ build: () -> N) {
self.node = build()
}
@inlinable
public init<N: NodeRepresentable>(_ node: N) {
self.node = node
}
@inlinable
public func render() -> String {
node.render()
}
}
public extension NodeRepresentable {
@inlinable
func eraseToAnyNode() -> AnyNode {
.init(self)
}
}

View File

@@ -0,0 +1,38 @@
public struct Group: NodeRepresentable {
@usableFromInline
let node: any NodeRepresentable
@usableFromInline
init(
separator: AnyNode,
node: any NodeRepresentable
) {
if let many = node as? _ManyNode {
self.node = _ManyNode(many.nodes, separator: separator)
} else {
self.node = node
}
}
@inlinable
public init(
separator: any NodeRepresentable,
@NodeBuilder _ build: () -> any NodeRepresentable
) {
self.init(separator: separator.eraseToAnyNode(), node: build())
}
@inlinable
public init(
separator: AnyNode = Space().eraseToAnyNode(),
@NodeBuilder _ build: () -> any NodeRepresentable
) {
self.init(separator: separator, node: build())
}
@inlinable
public func render() -> String {
node.render()
}
}

View File

@@ -0,0 +1,32 @@
@preconcurrency import Rainbow
public struct Header: NodeRepresentable {
@usableFromInline
let node: Text
@usableFromInline
let styles: [Style]
@inlinable
public init(
_ text: String,
color: NamedColor? = .yellow,
styles: [Style] = [.bold]
) {
var text = Text(text)
if let color {
text = text.color(color)
}
self.node = text
self.styles = styles
}
@inlinable
public func render() -> String {
styles.reduce(node) { node, style in
node.style(style)
}
.render()
}
}

View File

@@ -0,0 +1,34 @@
public extension NodeRepresentable {
@inlinable
func repeating(_ count: Int) -> some NodeRepresentable {
RepeatingNode(self, count: count)
}
}
@usableFromInline
struct RepeatingNode: NodeRepresentable {
@usableFromInline
let node: any NodeRepresentable
@usableFromInline
let count: Int
@usableFromInline
init(_ node: any NodeRepresentable, count: Int) {
self.node = node
self.count = count
}
@usableFromInline
func render() -> String {
var string = node.render()
guard count > 0 else { return string }
var count = count - 1
while count > 0 {
string = "\(string)\(node.render())"
count -= 1
}
return string
}
}

View File

@@ -0,0 +1,41 @@
// TODO: Remove init(header:...) initializers.
public struct Section: NodeRepresentable {
@usableFromInline
let node: any NodeRepresentable
@inlinable
public init<N: NodeRepresentable>(@NodeBuilder _ build: () -> N) {
self.node = build()
}
@inlinable
public func render() -> String {
node.render()
}
@inlinable
public init<Content: NodeRepresentable, Separator: NodeRepresentable>(
header: String,
separator: Separator,
@NodeBuilder content: () -> Content
) {
self.init {
Text(header)
separator
content()
}
}
@inlinable
public init<Content: NodeRepresentable>(
header: String,
inline: Bool = false,
@NodeBuilder content: () -> Content
) {
self.init(
header: header,
separator: inline ? Space().eraseToAnyNode() : NewLine().eraseToAnyNode(),
content: content
)
}
}

View File

@@ -0,0 +1,69 @@
@preconcurrency import Rainbow
public struct Text: NodeRepresentable {
@usableFromInline
let text: String
@inlinable
public init(_ text: String) {
self.text = text
}
@inlinable
public func render() -> String {
text
}
@inlinable
public func color(_ color: NamedColor) -> Self {
.init(text.applyingColor(color))
}
@inlinable
public func style(_ style: Style) -> Self {
.init(text.applyingStyle(style))
}
}
public struct NewLine: NodeRepresentable {
@usableFromInline
let node: any NodeRepresentable
@inlinable
public init(count: Int = 1) {
self.node = Text("\n").repeating(count)
}
@inlinable
public func render() -> String {
node.render()
}
}
public struct Space: NodeRepresentable {
let node: any NodeRepresentable
public init(count: Int = 1) {
self.node = Text(" ").repeating(count)
}
public func render() -> String {
node.render()
}
}
public extension NodeRepresentable {
static func empty() -> Self where Self == Text {
Text("")
}
static func newLine(count: Int = 1) -> Self where Self == NewLine {
NewLine(count: count)
}
static func space(count: Int = 1) -> Self where Self == Space {
Space(count: count)
}
}