Exploring new style
This commit is contained in:
@@ -16,11 +16,20 @@ let package = Package(
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "Rainbow", package: "Rainbow")
|
.product(name: "Rainbow", package: "Rainbow")
|
||||||
]
|
]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "CliDoc2",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "Rainbow", package: "Rainbow")
|
||||||
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "CliDocTests",
|
name: "CliDocTests",
|
||||||
dependencies: ["CliDoc"]
|
dependencies: ["CliDoc"]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "CliDoc2Tests",
|
||||||
|
dependencies: ["CliDoc2"]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,15 +17,17 @@ public extension NodeModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension Node {
|
public extension Node {
|
||||||
|
|
||||||
func modifier<T: NodeModifier>(_ modifier: T) -> ModifiedNode<Self, T> {
|
func modifier<T: NodeModifier>(_ modifier: T) -> ModifiedNode<Self, T> {
|
||||||
.init(content: self, modifier: modifier)
|
.init(content: self, modifier: modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where Content: Node,
|
public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where
|
||||||
Modifier1: NodeModifier, Modifier2: NodeModifier,
|
Content: Node,
|
||||||
Modifier1.Content == Content, Modifier1.Body == Modifier2.Content
|
Modifier1: NodeModifier,
|
||||||
|
Modifier2: NodeModifier,
|
||||||
|
Modifier1.Content == Content,
|
||||||
|
Modifier1.Body == Modifier2.Content
|
||||||
{
|
{
|
||||||
let first: Modifier1
|
let first: Modifier1
|
||||||
let second: Modifier2
|
let second: Modifier2
|
||||||
@@ -33,7 +35,6 @@ public struct ConcatModifier<Content, Modifier1, Modifier2>: NodeModifier where
|
|||||||
public func render(content: Content) -> some Node {
|
public func render(content: Content) -> some Node {
|
||||||
second.render(content: first.render(content: content))
|
second.render(content: first.render(content: content))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ModifiedNode<Content, Modifier> {
|
public struct ModifiedNode<Content, Modifier> {
|
||||||
@@ -54,7 +55,7 @@ extension ModifiedNode: Node where Content: Node,
|
|||||||
Modifier: NodeModifier,
|
Modifier: NodeModifier,
|
||||||
Modifier.Content == Content
|
Modifier.Content == Content
|
||||||
{
|
{
|
||||||
public var body: some Node {
|
public var body: Content {
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,11 +71,3 @@ extension ModifiedNode: NodeModifier where
|
|||||||
return modifier.render(content: body).render()
|
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
149
Sources/CliDoc2/Node.swift
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Tests/CliDoc2Tests/CliDoc2Tests.swift
Normal file
31
Tests/CliDoc2Tests/CliDoc2Tests.swift
Normal 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))
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ func checkLabelColorModifier() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.labelColor(.blue)
|
.labelColor(.blue)
|
||||||
// .labelColor(.green)
|
|
||||||
print(type(of: group))
|
print(type(of: group))
|
||||||
print(type(of: group.body))
|
print(type(of: group.body))
|
||||||
|
|
||||||
@@ -52,6 +52,13 @@ func checkLabelColorModifier() {
|
|||||||
\("Bang".green) boom
|
\("Bang".green) boom
|
||||||
"""
|
"""
|
||||||
#expect(group.render() == expected)
|
#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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user