Extend IMAPClientProtocol with storeFlags, moveMessage, copyMessage, expunge, appendMessage, capabilities methods. Implement all six in IMAPClient actor using NIOIMAPCore typed commands. Add multi-part command support to IMAPConnection/IMAPCommandRunner for APPEND. MockIMAPClient tracks all write calls for testing. SyncCoordinator detects mailbox roles from LIST attributes with name-based fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.1 KiB
Swift
40 lines
1.1 KiB
Swift
import NIOIMAPCore
|
|
|
|
struct IMAPCommandRunner {
|
|
private let connection: IMAPConnection
|
|
private var tagCounter: Int = 0
|
|
|
|
init(connection: IMAPConnection) {
|
|
self.connection = connection
|
|
}
|
|
|
|
mutating func nextTag() -> String {
|
|
tagCounter += 1
|
|
return "A\(tagCounter)"
|
|
}
|
|
|
|
mutating func run(_ command: Command) async throws -> [Response] {
|
|
let tag = nextTag()
|
|
let tagged = TaggedCommand(tag: tag, command: command)
|
|
return try await connection.sendCommand(tag, command: .tagged(tagged))
|
|
}
|
|
|
|
mutating func runAppend(_ parts: [AppendCommand]) async throws -> [Response] {
|
|
let tag = nextTag()
|
|
var streamParts: [CommandStreamPart] = []
|
|
for (index, part) in parts.enumerated() {
|
|
if index == 0 {
|
|
// Replace the start command's tag with our generated tag
|
|
if case .start(_, let mailbox) = part {
|
|
streamParts.append(.append(.start(tag: tag, appendingTo: mailbox)))
|
|
} else {
|
|
streamParts.append(.append(part))
|
|
}
|
|
} else {
|
|
streamParts.append(.append(part))
|
|
}
|
|
}
|
|
return try await connection.sendMultiPartCommand(tag, parts: streamParts)
|
|
}
|
|
}
|