Exploring new style

This commit is contained in:
2024-12-03 07:53:12 -05:00
parent 3d82eed389
commit 8658035d51
5 changed files with 205 additions and 16 deletions

View File

@@ -16,11 +16,20 @@ let package = Package(
dependencies: [
.product(name: "Rainbow", package: "Rainbow")
]
),
.target(
name: "CliDoc2",
dependencies: [
.product(name: "Rainbow", package: "Rainbow")
]
),
.testTarget(
name: "CliDocTests",
dependencies: ["CliDoc"]
),
.testTarget(
name: "CliDoc2Tests",
dependencies: ["CliDoc2"]
)
]
)

View File

@@ -17,15 +17,17 @@ public extension NodeModifier {
}
public extension Node {
func modifier<T: NodeModifier>(_ modifier: T) -> ModifiedNode<Self, T> {
.init(content: self, modifier: modifier)
}
}
public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where Content: Node,
Modifier1: NodeModifier, Modifier2: NodeModifier,
Modifier1.Content == Content, Modifier1.Body == Modifier2.Content
public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where
Content: Node,
Modifier1: NodeModifier,
Modifier2: NodeModifier,
Modifier1.Content == Content,
Modifier1.Body == Modifier2.Content
{
let first: Modifier1
let second: Modifier2
@@ -33,7 +35,6 @@ public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where
public func render(content: Content) -> some Node {
second.render(content: first.render(content: content))
}
}
public struct ModifiedNode<Content, Modifier> {
@@ -54,7 +55,7 @@ extension ModifiedNode: Node where Content: Node,
Modifier: NodeModifier,
Modifier.Content == Content
{
public var body: some Node {
public var body: Content {
content
}
}
@@ -70,11 +71,3 @@ extension ModifiedNode: NodeModifier where
return modifier.render(content: body).render()
}
}
extension ModifiedNode where Modifier: NodeModifier {
func modifier<T: NodeModifier>(_ modifier: T) -> ModifiedNode<Content, ConcatModifier<Content, Modifier, T>> {
.init(content: content, modifier: self.modifier.concat(modifier))
}
}

149
Sources/CliDoc2/Node.swift Normal file
View File

@@ -0,0 +1,149 @@
import Rainbow
public protocol Node {
func render() -> String
}
extension String: Node {
public func render() -> String {
self
}
}
@resultBuilder
enum NodeBuilder {
public static func buildPartialBlock<N: Node>(first: N) -> N {
first
}
static func buildPartialBlock<N0: Node, N1: Node>(accumulated: N0, next: N1) -> ManyNode {
.init(nodes: [accumulated, next])
}
public static func buildArray<N: Node>(_ components: [N]) -> ManyNode {
.init(nodes: components)
}
static func buildBlock<N: Node>(_ components: N...) -> ManyNode {
.init(nodes: components)
}
static func buildEither<N: Node>(first component: N) -> N {
component
}
static func buildEither<N: Node>(second component: N) -> N {
component
}
static func buildOptional<N: Node>(_ component: N?) -> any Node {
component
}
}
extension Optional: Node where Wrapped: Node {
public func render() -> String {
guard let node = self else { return "" }
return node.render()
}
}
struct ManyNode: Node {
var nodes: [any Node]
init(nodes: [any Node]) {
self.nodes = nodes.reduce(into: [any Node]()) { array, next in
if let many = next as? ManyNode {
array += many.nodes
} else {
array.append(next)
}
}
}
func render() -> String {
nodes.reduce("") { $0 + $1.render() }
}
}
struct Group: Node {
var content: [any Node]
var separator: any Node
init(
content: [any Node],
separator: any Node = "\n"
) {
self.content = content
self.separator = separator
}
init(
separator: any Node = "\n",
@NodeBuilder content: () -> any Node
) {
let content = content()
if let many = content as? ManyNode {
self.content = many.nodes
} else {
self.content = [content]
}
self.separator = separator
}
func render() -> String {
content.reduce("") {
$0 + $1.render() + separator.render()
}
}
}
struct ColorNode: Node {
let color: NamedColor
let node: any Node
func render() -> String {
node.render().applyingColor(color)
}
}
extension Node {
func color(_ color: NamedColor) -> some Node {
ColorNode(color: color, node: self)
}
}
struct Label: Node {
var node: any Node
init(@NodeBuilder _ content: () -> any Node) {
self.node = content()
}
func render() -> String {
node.render()
}
}
struct Note: Node {
var label: any Node
var content: any Node
var separator: any Node = " "
init(
separator: any Node = " ",
@NodeBuilder _ label: () -> any Node,
@NodeBuilder content: () -> any Node
) {
self.separator = separator
self.label = label()
self.content = content()
}
func render() -> String {
Group(content: [label, content], separator: separator).render()
}
}

View File

@@ -0,0 +1,31 @@
@testable import CliDoc2
@preconcurrency import Rainbow
import Testing
let setupRainbow: Bool = {
Rainbow.enabled = true
Rainbow.outputTarget = .console
return true
}()
@Test
func testGroup() {
#expect(setupRainbow)
let group = Group {
Label { "Foo:" }.color(.blue)
"Bar"
"Baz"
Note { "Bang:" } content: { "boom" }
if setupRainbow {
Label("Hello, rainbow").color(.blue)
} else {
Label("No color for you!").color(.green)
}
}.color(.green)
print(group.render())
let note = Note { "Bang:" } content: { "boom" }
print(note.render())
print(type(of: note.label))
}

View File

@@ -41,7 +41,7 @@ func checkLabelColorModifier() {
}
}
.labelColor(.blue)
// .labelColor(.green)
print(type(of: group))
print(type(of: group.body))
@@ -52,6 +52,13 @@ func checkLabelColorModifier() {
\("Bang".green) boom
"""
#expect(group.render() == expected)
// var foo = group
// if var bar = group as? ModifiedNode<Group, GroupLabelModifier> {
// print("Modified Node")
// bar.modifier = bar.modifier.concat(GroupLabelModifier(color: .green))
// print(type(of: bar.body))
// }
}
@Test