Skip to content

Commit

Permalink
Support dynamic select (#7)
Browse files Browse the repository at this point in the history
* Support dynamic select

* Can take various sequences

* Rename to `any` from `forEach`

* Add an overload that can take variadic arguments
  • Loading branch information
Kuniwak authored Jul 7, 2024
1 parent 679ee7d commit 394b971
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ The loop will break when the channel is closed.

`none { ... }` if none of the channel operations were ready, none will execute instead.

`any(x1, x2, ...) { x in ... }` or `any(seq) { el in ... }` operates on a sequence and is useful for working with an array of channels.

### Examples

```swift
Expand Down
39 changes: 25 additions & 14 deletions Sources/AsyncChannels/Select.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ struct SendHandler<T>: SelectProtocol {

@resultBuilder
public struct SelectCollector {
public static func buildBlock(_ handlers: SelectHandler...) -> [SelectHandler] {
return handlers
public static func buildBlock(_ handlers: [SelectHandler]...) -> [SelectHandler] {
return handlers.reduce([], +)
}
}

Expand Down Expand Up @@ -118,37 +118,48 @@ public func select(@SelectCollector cases: () -> ([SelectHandler])) async {

@inlinable
@inline(__always)
public func receive<T>(_ chan: Channel<T>, _ outFunc: @escaping (T?) async -> ()) -> SelectHandler {
return SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: outFunc))
public func receive<T>(_ chan: Channel<T>, _ outFunc: @escaping (T?) async -> ()) -> [SelectHandler] {
return [SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: outFunc))]
}

@inlinable
@inline(__always)
public func receive<T>(_ chan: Channel<T>, _ outFunc: @escaping () async -> ()) -> SelectHandler {
return SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: { _ in await outFunc() }))
public func receive<T>(_ chan: Channel<T>, _ outFunc: @escaping () async -> ()) -> [SelectHandler] {
return [SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: { _ in await outFunc() }))]
}

@inlinable
@inline(__always)
public func receive<T>(_ chan: Channel<T>) -> SelectHandler {
return SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: { _ in }))
public func receive<T>(_ chan: Channel<T>) -> [SelectHandler] {
return [SelectHandler(inner: ReceiveHandler(chan: chan, outFunc: { _ in }))]
}

@inlinable
@inline(__always)
public func send<T>(_ val: T, to chan: Channel<T>) -> SelectHandler {
return SelectHandler(inner: SendHandler(chan: chan, val: val, onSend: {}))
public func send<T>(_ val: T, to chan: Channel<T>) -> [SelectHandler] {
return [SelectHandler(inner: SendHandler(chan: chan, val: val, onSend: {}))]
}

@inlinable
@inline(__always)
public func send<T>(_ val: T, to chan: Channel<T>, _ onSend: @escaping () async -> ()) -> SelectHandler {
return SelectHandler(inner: SendHandler(chan: chan, val: val, onSend: onSend))
public func send<T>(_ val: T, to chan: Channel<T>, _ onSend: @escaping () async -> ()) -> [SelectHandler] {
return [SelectHandler(inner: SendHandler(chan: chan, val: val, onSend: onSend))]
}

@inlinable
@inline(__always)
public func none(handler: @escaping () async -> ()) -> SelectHandler {
return SelectHandler(inner: NoneHandler(handler: handler))
public func none(handler: @escaping () async -> ()) -> [SelectHandler] {
return [SelectHandler(inner: NoneHandler(handler: handler))]
}

@inlinable
@inline(__always)
public func any<S, T>(_ seq: S, @SelectCollector cases: (T) -> ([SelectHandler])) -> [SelectHandler] where S: Sequence, S.Element == T {
return seq.flatMap { cases($0) }
}

@inlinable
@inline(__always)
public func any<T>(_ elements: T..., @SelectCollector cases: (T) -> ([SelectHandler])) -> [SelectHandler] {
return elements.flatMap { cases($0) }
}
54 changes: 54 additions & 0 deletions Tests/AsyncChannelsTests/AsyncChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,61 @@ final class AsyncTest: XCTestCase {
let r = await result.reduce(into: []) { $0.append($1) }
XCTAssertEqual(["foo", "bar"].sorted(), r.sorted())
}

func testDynamicSelect() async {
let a = Channel<String>()
let b = Channel<String>()
let result = Channel<String>(capacity: 2)

Task {
await a <- "foo"
await b <- "bar"
}

await select {
any([a, b]) {
receive($0) { await result <- $0! }
}
}

await select {
any([a, b]) {
receive($0) { await result <- $0! }
}
}
result.close()

let r = await result.reduce(into: []) { $0.append($1) }
XCTAssertEqual(["foo", "bar"].sorted(), r.sorted())
}

func testDynamicVariadicSelect() async {
let a = Channel<String>()
let b = Channel<String>()
let result = Channel<String>(capacity: 2)

Task {
await a <- "foo"
await b <- "bar"
}

await select {
any(a, b) {
receive($0) { await result <- $0! }
}
}

await select {
any(a, b) {
receive($0) { await result <- $0! }
}
}
result.close()

let r = await result.reduce(into: []) { $0.append($1) }
XCTAssertEqual(["foo", "bar"].sorted(), r.sorted())
}

func testBufferSelect() async {
let c = Channel<String>(capacity: 3)
let d = Channel<String>(capacity: 3)
Expand Down

0 comments on commit 394b971

Please sign in to comment.