commit
e693d4dcc8
|
@ -23,9 +23,14 @@ let package = Package(
|
|||
name: "Chain",
|
||||
dependencies: [
|
||||
"E"
|
||||
]),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "ChainTests",
|
||||
dependencies: ["Chain"]),
|
||||
dependencies: [
|
||||
"E",
|
||||
"Chain"
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -13,62 +13,115 @@ public extension Chain {
|
|||
func run(
|
||||
name: String? = nil,
|
||||
input: Variable? = nil,
|
||||
shouldFlattenOutput: Bool = false
|
||||
logging: Bool = false
|
||||
) -> Variable {
|
||||
var logInfo: String {
|
||||
"[\(Date())] Chain\(name.map { " (\($0)) "} ?? ""):"
|
||||
}
|
||||
var output: Variable = .array([])
|
||||
|
||||
log(functionName: "run",
|
||||
name: name,
|
||||
logging: logging)
|
||||
|
||||
switch self {
|
||||
case .end:
|
||||
print("\(logInfo) End")
|
||||
|
||||
output = output.update {
|
||||
.array($0 + [Variable.void])
|
||||
}
|
||||
case .complete(let completion):
|
||||
print("\(logInfo) Complete")
|
||||
|
||||
output = output.update {
|
||||
.array($0 + [completion?.run(input) ?? Variable.void])
|
||||
}
|
||||
case .link(let action,
|
||||
let next):
|
||||
print("\(logInfo) Link")
|
||||
|
||||
let actionOutput: Variable = action.run(input) ?? Variable.void
|
||||
|
||||
output = output.update {
|
||||
.array($0 + [actionOutput] + [next.run(name: name, input: actionOutput)])
|
||||
.array($0 + [actionOutput] + [next.run(name: name,
|
||||
input: actionOutput,
|
||||
logging: logging)])
|
||||
}
|
||||
case .background(let action,
|
||||
let next):
|
||||
print("\(logInfo) Background")
|
||||
DispatchQueue.global().async {
|
||||
let actionOutput: Variable = action.run(input) ?? Variable.void
|
||||
|
||||
output = output.update {
|
||||
.array($0 + [actionOutput])
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
output = output.update {
|
||||
.array($0 + [next.run(name: name, input: actionOutput)])
|
||||
}
|
||||
_ = next.run(name: name,
|
||||
input: actionOutput,
|
||||
logging: logging)
|
||||
|
||||
}
|
||||
}
|
||||
case .multi(let links):
|
||||
print("\(logInfo) Multi")
|
||||
output = output.update {
|
||||
.array($0 + links.map { $0.run(name: name) })
|
||||
.array($0 + links.map { $0.run(name: name,
|
||||
logging: logging) })
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten Output
|
||||
if shouldFlattenOutput {
|
||||
return output.flatten
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func runHead(
|
||||
name: String? = nil,
|
||||
input: Variable? = nil,
|
||||
logging: Bool = false
|
||||
) -> Variable {
|
||||
log(functionName: "runHead",
|
||||
name: name,
|
||||
logging: logging)
|
||||
|
||||
switch self {
|
||||
case .end:
|
||||
return .void
|
||||
case .complete(let function):
|
||||
return function?.run(input) ?? .void
|
||||
case .background(let function, _):
|
||||
return Chain.background(function, .end).run(name: name, input: input, logging: logging)
|
||||
case .link(let function, _):
|
||||
return function.run(input) ?? .void
|
||||
case .multi(let chains):
|
||||
return .array(chains.compactMap { $0.runHead(input: input) })
|
||||
}
|
||||
}
|
||||
|
||||
func dropHead() -> Chain? {
|
||||
switch self {
|
||||
case .end, .complete(_):
|
||||
return nil
|
||||
case .background(_, let next), .link(_, let next):
|
||||
return next
|
||||
case .multi(let chains):
|
||||
guard !chains.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return .multi(chains.compactMap { $0.dropHead() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension Chain {
|
||||
func log(
|
||||
functionName: String,
|
||||
name: String?,
|
||||
logging: Bool
|
||||
) {
|
||||
var logInfo: String {
|
||||
"[\(Date())] Chain.\(functionName)\(name.map { " (\($0))"} ?? ""):"
|
||||
}
|
||||
|
||||
if logging {
|
||||
switch self {
|
||||
case .end:
|
||||
print("\(logInfo) End")
|
||||
case .complete:
|
||||
print("\(logInfo) Complete")
|
||||
case .link:
|
||||
print("\(logInfo) Link")
|
||||
case .background:
|
||||
print("\(logInfo) Background")
|
||||
case .multi:
|
||||
print("\(logInfo) Multi")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import XCTest
|
||||
import E
|
||||
@testable import Chain
|
||||
|
||||
final class ChainTests: XCTestCase {
|
||||
|
@ -7,39 +8,51 @@ final class ChainTests: XCTestCase {
|
|||
var isLooping = false
|
||||
|
||||
let output = Chain.link(
|
||||
.void { print(0) },
|
||||
.out {
|
||||
print(0)
|
||||
return 0
|
||||
},
|
||||
.link(
|
||||
.void { print(1) },
|
||||
.out {
|
||||
print(1)
|
||||
return 1
|
||||
},
|
||||
.multi(
|
||||
[
|
||||
.background(
|
||||
.void {
|
||||
print("Loading...")
|
||||
sleep(5)
|
||||
sleep(1)
|
||||
print("Loading Done!")
|
||||
isLooping = false
|
||||
},
|
||||
.complete(.void {
|
||||
XCTAssertEqual(isLooping, false)
|
||||
})
|
||||
.complete(
|
||||
.out {
|
||||
XCTAssertEqual(isLooping, false)
|
||||
|
||||
return .string("Done Loading")
|
||||
}
|
||||
)
|
||||
),
|
||||
|
||||
.link(
|
||||
.void {
|
||||
.out {
|
||||
isLooping = true
|
||||
while isLooping { }
|
||||
return .string("Done Looping")
|
||||
},
|
||||
.complete(.void {
|
||||
text = "Hello, World!"
|
||||
})
|
||||
.complete(
|
||||
.out {
|
||||
text = "Hello, World!"
|
||||
return "Complete"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
.run(name: "ChainTests-testExample")
|
||||
.run(name: "ChainTests-testExample", logging: true)
|
||||
|
||||
XCTAssertEqual(text, "Hello, World!")
|
||||
|
||||
|
@ -54,33 +67,41 @@ final class ChainTests: XCTestCase {
|
|||
func testOutput() {
|
||||
let output = Chain.link(
|
||||
.out { "First" },
|
||||
.link( .in {
|
||||
print("Value: \($0)")
|
||||
}, .multi(
|
||||
[
|
||||
.multi([
|
||||
.end,
|
||||
.end,
|
||||
.end
|
||||
]),
|
||||
.link(.out {
|
||||
"Link"
|
||||
}, .link(
|
||||
.out { "Last" },
|
||||
.complete(.inout { value in
|
||||
guard case .string(let value) = value else {
|
||||
XCTFail()
|
||||
return .void
|
||||
}
|
||||
|
||||
return .string("\(value) !!!")
|
||||
})
|
||||
))
|
||||
]
|
||||
))
|
||||
|
||||
.link(
|
||||
.in {
|
||||
print("Value: \($0)")
|
||||
},
|
||||
.multi(
|
||||
[
|
||||
.multi(
|
||||
[
|
||||
.end,
|
||||
.end,
|
||||
.end
|
||||
]
|
||||
),
|
||||
.link(
|
||||
.out { "Link" },
|
||||
.link(
|
||||
.out { "Last" },
|
||||
.complete(
|
||||
.inout { value in
|
||||
guard case .string(let value) = value else {
|
||||
XCTFail()
|
||||
return .void
|
||||
}
|
||||
|
||||
return .string("\(value) !!!")
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
.run(name: "ChainTests-testOutput", shouldFlattenOutput: true)
|
||||
.run(name: "ChainTests-testOutput", logging: true)
|
||||
.flatten
|
||||
|
||||
guard case .array(let values) = output else {
|
||||
XCTFail()
|
||||
|
@ -92,7 +113,54 @@ final class ChainTests: XCTestCase {
|
|||
XCTAssertEqual(values.count, 8)
|
||||
}
|
||||
|
||||
func testChainStep() {
|
||||
let chain = Chain.link(
|
||||
.out {
|
||||
"First"
|
||||
},
|
||||
.link(
|
||||
.inout {
|
||||
.string("Value: \($0)")
|
||||
},
|
||||
.end
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(chain.run(logging: true).flatten, .array([.string("First"), .string("Value: string(\"First\")"), .void]))
|
||||
XCTAssertEqual(chain.runHead(logging: true), .string("First"))
|
||||
XCTAssertEqual(chain.dropHead()?.runHead(input: .float(3.14), logging: true), .string("Value: float(3.14)"))
|
||||
}
|
||||
|
||||
func testBackgroundOutput() {
|
||||
let chain = Chain.link(
|
||||
.out {
|
||||
"First"
|
||||
},
|
||||
.background(
|
||||
.inout {
|
||||
sleep(3)
|
||||
return .string("Value: \($0)")
|
||||
},
|
||||
.complete(
|
||||
.inout {
|
||||
print("HERE: \($0)")
|
||||
return "HERE"
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(chain.run(name: "chain.run", logging: true).flatten, .array([.string("First")]))
|
||||
XCTAssertEqual(chain.runHead(name: "chain.runHead", logging: true), .string("First"))
|
||||
XCTAssertEqual(chain.dropHead()?.runHead(name: "chain.dropHead()?.runHead", input: .float(3.14), logging: true), .array([]))
|
||||
|
||||
sleep(6)
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testExample", testExample),
|
||||
("testOutput", testOutput),
|
||||
("testChainStep", testChainStep),
|
||||
("testBackgroundOutput", testBackgroundOutput)
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue