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