Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamic select #7

Merged
merged 4 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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