From f37b287f5ef2e2d6d62463fce6a9bb0d68741150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Fri, 13 Mar 2026 18:11:24 +0100 Subject: [PATCH] add models module: shared types for accounts, messages, threads, sync fix Package.swift: remove NIOIMAPCore product reference (only NIOIMAP is exported by swift-nio-imap) Co-Authored-By: Claude Opus 4.6 --- Packages/MagnumOpusCore/Package.swift | 2 - .../Sources/Models/AccountConfig.swift | 15 +++++++ .../Sources/Models/Credentials.swift | 9 ++++ .../Sources/Models/EmailAddress.swift | 32 +++++++++++++++ .../Sources/Models/MailboxInfo.swift | 27 ++++++++++++ .../Sources/Models/MessageSummary.swift | 41 +++++++++++++++++++ .../Sources/Models/Placeholder.swift | 1 - .../Sources/Models/SyncEvent.swift | 9 ++++ .../Sources/Models/SyncState.swift | 5 +++ .../Sources/Models/ThreadSummary.swift | 26 ++++++++++++ .../Tests/ModelsTests/EmailAddressTests.swift | 34 +++++++++++++++ .../Tests/ModelsTests/Placeholder.swift | 1 - 12 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 Packages/MagnumOpusCore/Sources/Models/AccountConfig.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/Credentials.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/EmailAddress.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/MailboxInfo.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/MessageSummary.swift delete mode 100644 Packages/MagnumOpusCore/Sources/Models/Placeholder.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/SyncEvent.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/SyncState.swift create mode 100644 Packages/MagnumOpusCore/Sources/Models/ThreadSummary.swift create mode 100644 Packages/MagnumOpusCore/Tests/ModelsTests/EmailAddressTests.swift delete mode 100644 Packages/MagnumOpusCore/Tests/ModelsTests/Placeholder.swift diff --git a/Packages/MagnumOpusCore/Package.swift b/Packages/MagnumOpusCore/Package.swift index c45b215..2f285d1 100644 --- a/Packages/MagnumOpusCore/Package.swift +++ b/Packages/MagnumOpusCore/Package.swift @@ -32,7 +32,6 @@ let package = Package( name: "IMAPClient", dependencies: [ "Models", - .product(name: "NIOIMAPCore", package: "swift-nio-imap"), .product(name: "NIOIMAP", package: "swift-nio-imap"), .product(name: "NIOSSL", package: "swift-nio-ssl"), ] @@ -47,7 +46,6 @@ let package = Package( name: "IMAPClientTests", dependencies: [ "IMAPClient", - .product(name: "NIOIMAPCore", package: "swift-nio-imap"), ] ), .testTarget(name: "SyncEngineTests", dependencies: ["SyncEngine", "IMAPClient", "MailStore"]), diff --git a/Packages/MagnumOpusCore/Sources/Models/AccountConfig.swift b/Packages/MagnumOpusCore/Sources/Models/AccountConfig.swift new file mode 100644 index 0000000..3d141d0 --- /dev/null +++ b/Packages/MagnumOpusCore/Sources/Models/AccountConfig.swift @@ -0,0 +1,15 @@ +public struct AccountConfig: Sendable, Codable, Equatable { + public var id: String + public var name: String + public var email: String + public var imapHost: String + public var imapPort: Int + + public init(id: String, name: String, email: String, imapHost: String, imapPort: Int) { + self.id = id + self.name = name + self.email = email + self.imapHost = imapHost + self.imapPort = imapPort + } +} diff --git a/Packages/MagnumOpusCore/Sources/Models/Credentials.swift b/Packages/MagnumOpusCore/Sources/Models/Credentials.swift new file mode 100644 index 0000000..783b7b2 --- /dev/null +++ b/Packages/MagnumOpusCore/Sources/Models/Credentials.swift @@ -0,0 +1,9 @@ +public struct Credentials: Sendable { + public var username: String + public var password: String + + public init(username: String, password: String) { + self.username = username + self.password = password + } +} diff --git a/Packages/MagnumOpusCore/Sources/Models/EmailAddress.swift b/Packages/MagnumOpusCore/Sources/Models/EmailAddress.swift new file mode 100644 index 0000000..aacc3a6 --- /dev/null +++ b/Packages/MagnumOpusCore/Sources/Models/EmailAddress.swift @@ -0,0 +1,32 @@ +import Foundation + +public struct EmailAddress: Sendable, Codable, Equatable, Hashable { + public var name: String? + public var address: String + + public init(name: String? = nil, address: String) { + self.name = name + self.address = address + } + + public var displayName: String { + name ?? address + } + + /// Parses "Alice " or bare "alice@example.com" + public static func parse(_ raw: String) -> EmailAddress { + let trimmed = raw.trimmingCharacters(in: .whitespaces) + guard let openAngle = trimmed.lastIndex(of: "<"), + let closeAngle = trimmed.lastIndex(of: ">"), + openAngle < closeAngle + else { + return EmailAddress(address: trimmed) + } + let addr = String(trimmed[trimmed.index(after: openAngle)..") + #expect(addr.name == "Alice") + #expect(addr.address == "alice@example.com") + #expect(addr.displayName == "Alice") + } + + @Test("parses bare email address") + func parsesBareAddress() { + let addr = EmailAddress.parse("bob@example.com") + #expect(addr.name == nil) + #expect(addr.address == "bob@example.com") + #expect(addr.displayName == "bob@example.com") + } + + @Test("parses quoted name with angle brackets") + func parsesQuotedName() { + let addr = EmailAddress.parse("\"Bob Smith\" ") + #expect(addr.name == "Bob Smith") + #expect(addr.address == "bob@example.com") + } + + @Test("handles empty string gracefully") + func handlesEmpty() { + let addr = EmailAddress.parse("") + #expect(addr.address == "") + } +} diff --git a/Packages/MagnumOpusCore/Tests/ModelsTests/Placeholder.swift b/Packages/MagnumOpusCore/Tests/ModelsTests/Placeholder.swift deleted file mode 100644 index d196bca..0000000 --- a/Packages/MagnumOpusCore/Tests/ModelsTests/Placeholder.swift +++ /dev/null @@ -1 +0,0 @@ -import Testing