Alternatives to wrapping NIOCore.Channel?

one pattern i find myself doing a lot of is making thin wrappers around any Channel like:

public
struct MongoChannel:Sendable
{
    @usableFromInline
    let channel:any Channel

    public
    init(_ channel:any Channel)
    {
        self.channel = channel
    }
}

this is because i have many APIs that only make sense when called on a pipeline that has an initial handler with a specific OutboundIn type.

extension MongoChannel
{
    public
    func interrupt()
    {
        self.channel.writeAndFlush(Action.interrupt, promise: nil)
    }

    @usableFromInline
    func timeout(by deadline:ContinuousClock.Instant) async throws
    {
        try await Task.sleep(until: deadline, clock: .continuous)
        self.channel.writeAndFlush(Action.timeout, promise: nil)
    }
}

but i often still need the NIOCore.Channel APIs, so i just end up creating shims for all the Channel APIs. it seems that the root cause of this problem is there is simply no way to constrain Channel API by initial OutboundIn type.

are there any good alternatives to this pattern?

Currently, I'm afraid the answer is "no". NIO's Channels cannot become generic now, so there's no place for you to attach this type information. Writing a wrapper type that does this once is all you can do, sadly.

We're working on adding support for NIOAsyncChannel, which does have InboundIn and OutboundOut generic type parameters. This will eventually be able to solve your problem. See this PR.

2 Likes