apple / swift-nio-imap

A Swift project that provides an implementation of the IMAP4rev1 protocol, built upon SwiftNIO.
Apache License 2.0
97 stars 13 forks source link

`BODYSTRUCTURE` Types #76

Closed danieleggert closed 4 years ago

danieleggert commented 4 years ago

The types related to the bodystructure are essential to code trying do things such as

The body structure is very complex and recursive. Here’s an attempt at making it a bit more approachable.

Note: I’m being a bit lax about the use of String vs. NString here — need to revise that.

Structure

As noted in #75 the top level structure should be something like

    public indirect enum BodyStructure: Equatable {
        case singlePart(BodyStructure.SinglePart)
        case multiPart(BodyStructure.MultiPart)
    }

Then, generally, nest body structure related types inside BodyStructure.

Single Part

A single part would look like this:

extension BodyStructure {
    public struct SinglePart: Equatable {
        public var bodyFields: Fields
        public var kind: Kind
        public var extension: BodyStructure.SinglePart.ExtensionData?
        public var mediaType: MediaType { get } // derived from `kind`

        public enum Kind: Equatable {
            /// A RFC 822 message, i.e. `"MESSAGE" "RFC822"`
            case rfc822Message(BodyStructure .RFC822Message)
            /// A message that is text, i.e. `"TEXT" "<subtype>"`
            case text(BodyStructure.Text)
            /// A message that is not an RFC 822 message, i.e. `"<type>" "<subtype>"`
            /// where `<subtype>` is not `RFC822`.
            case basic(BodyStructure.MediaType)
        }
    }
}

#### Single Part — Additional Types

This one is similar to existing`Body.Fields`:
```swift
extension BodyStructure {
    public struct Fields: Equatable {
        public var parameters: [FieldParameterPair]
        public var identifier: NString
        public var description: NString
        public var encoding: Encoding
        public var byteCount: UInt32
    }
}

with

extension BodyStructure {
    public struct FieldParameterPair: Equatable {
        public var field: String
        public var value: String
    }
}

See Body.FieldEncoding:

extension BodyStructure {
    public enum Encoding: Equatable {
        case sevenBit
        case eightBit
        case binary
        case base64
        case quotedPrintable
        case other(EncodedString)
    }
}
extension BodyStructure {
    public struct RFC822Message: Equatable {
        public var envelope: Envelope
        public var body: BodyStructure
        public var lineCount: Int
        public var mediaType: MediaType { get }
    }
}
extension BodyStructure {
    public struct TextMessage: Equatable {
        public var mediaSubtype: NString
        public var lineCount: Int
        public var mediaType: MediaType { get }
    }
}
public enum MediaType: Equatable {
    case text(subtype: EncodedString)
    case rfc822Message
    case application(subtype: EncodedString)
    case audio(subtype: EncodedString)
    case image(subtype: EncodedString)
    case message(subtype: EncodedString)
    case video(subtype: EncodedString)
    case other(type: EncodedString, subtype: EncodedString)
}

Note: This may have to be a struct for API stability.

extension BodyStructure.SinglePart {
    /// Extension data for a single part message.
    ///
    /// This is never returned with the `BODY` fetch,
    /// but can be returned with a `BODYSTRUCTURE` fetch.
    struct ExtensionData {
            /// MD5 of the body
            public var bodyDigest: EncodedString?
            /// RFC 2183 style content disposition
            public var disposition: MessageData.Disposition?
            /// A string or parenthesized list giving the body language
            /// value as defined in RFC 3066
            public var language: MessageData.LanguageIdentifier?
            /// A string list giving the body content URI as defined in RFC 2557
            public var location: MessageData.ContentLocation?
            public var extension: BodyStructure.Extension?
    }
}

The BodyStructure.Extension needs to represent body-extension — which is a string / number / (nested) array of string or number. I don’t know if this is actually used by anyone.

Multi Part

extension BodyStructure {
    public struct MultiPart: Equatable {
        public var parts: [BodyStructure]
        public var mediaSubtype: MediaSubtype
    }
}

extension BodyStructure.MultiPart {
    public enum MediaSubtype: Equatable {
        case alternative
        case related
        case mixed
        case other(String)
    }
}
extension BodyStructure.MultiPart {
    /// Extension data for a single part message.
    ///
    /// This is never returned with the `BODY` fetch,
    /// but can be returned with a `BODYSTRUCTURE` fetch.
    struct ExtensionData {
            public var parameters: [FieldParameterPair]
            /// RFC 2183 style content disposition
            public var disposition: MessageData.Disposition?
            /// A string or parenthesized list giving the body language
            /// value as defined in RFC 3066
            public var language: MessageData.LanguageIdentifier?
            /// A string list giving the body content URI as defined in RFC 2557
            public var location: MessageData.ContentLocation?
            public var extension: BodyStructure.Extension?
    }
}
Davidde94 commented 4 years ago

Alright I'm gonna split into a few issues and take them one-by-one :)

Davidde94 commented 4 years ago

Should be able to close this issue once #152 is merged.