Files

70 lines
1.8 KiB
Swift

import NIO
import NIOIMAPCore
import NIOIMAP
/// Events emitted by the IDLE handler
public enum IMAPIdleEvent: Sendable {
case exists(Int)
case expunge(Int)
case idleTerminated
}
/// NIO ChannelInboundHandler that processes untagged responses during IMAP IDLE.
/// Unlike the standard IMAPResponseHandler (which uses CheckedContinuation for tagged responses),
/// this handler uses AsyncStream to deliver a continuous stream of events.
final class IMAPIdleHandler: ChannelInboundHandler, RemovableChannelHandler, @unchecked Sendable {
typealias InboundIn = Response
private let continuation: AsyncStream<IMAPIdleEvent>.Continuation
private let idleTag: String
init(continuation: AsyncStream<IMAPIdleEvent>.Continuation, idleTag: String) {
self.continuation = continuation
self.idleTag = idleTag
}
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let response = unwrapInboundIn(data)
switch response {
case .untagged(let payload):
switch payload {
case .mailboxData(let data):
switch data {
case .exists(let count):
continuation.yield(.exists(count))
default:
break
}
case .messageData(let data):
switch data {
case .expunge(let seqNum):
continuation.yield(.expunge(Int(seqNum.rawValue)))
default:
break
}
default:
break
}
case .tagged(let tagged):
if tagged.tag == idleTag {
continuation.yield(.idleTerminated)
}
case .idleStarted:
// Server acknowledged IDLE we're now idling
break
case .fetch, .authenticationChallenge, .fatal:
break
}
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
continuation.finish()
context.close(promise: nil)
}
func channelInactive(context: ChannelHandlerContext) {
continuation.finish()
}
}