Skip to main content

imap_core/
ast.rs

1//! Borrowed AST for IMAP4rev1 / IMAP4rev2 server responses.
2//!
3//! All `&'a` references point into the original input buffer to preserve
4//! the parser's zero-copy contract. Quoted-string escape sequences (`\\`,
5//! `\"`) are NOT processed — the raw bytes are returned. Callers that need
6//! unescaped values should post-process.
7
8/// A single parsed server response line.
9#[derive(Debug, PartialEq, Eq)]
10pub enum Response<'a> {
11    /// A status response (`OK`, `NO`, `BAD`, `PREAUTH`, `BYE`), tagged or untagged.
12    Status(StatusResponse<'a>),
13    /// An untagged data response (`CAPABILITY`, `LIST`, `FETCH`, …).
14    Data(DataResponse<'a>),
15    /// A command-continuation request (`+ …`).
16    Continue(ContinueReq<'a>),
17}
18
19/// A status response: `<tag|*> <status> [code] text`.
20#[derive(Debug, PartialEq, Eq)]
21pub struct StatusResponse<'a> {
22    /// The command tag this response answers, or `None` for an untagged (`*`) response.
23    pub tag: Option<&'a str>,
24    /// The status condition (`OK`, `NO`, `BAD`, `PREAUTH`, `BYE`).
25    pub status: Status,
26    /// Optional machine-readable response code in `[...]`, if present.
27    pub code: Option<ResponseCode<'a>>,
28    /// Human-readable response text following the status (and code).
29    pub text: &'a str,
30}
31
32/// The status condition of a [`StatusResponse`].
33#[derive(Debug, PartialEq, Eq, Clone, Copy)]
34pub enum Status {
35    /// `OK` — success or informational.
36    Ok,
37    /// `NO` — operational error; the command failed.
38    No,
39    /// `BAD` — protocol error; the command was malformed or unexpected.
40    Bad,
41    /// `PREAUTH` — the connection is already authenticated (greeting only).
42    PreAuth,
43    /// `BYE` — the server is closing the connection.
44    Bye,
45}
46
47/// A machine-readable response code carried in `[...]` (RFC 9051 §7.1).
48#[derive(Debug, PartialEq, Eq)]
49pub enum ResponseCode<'a> {
50    /// `ALERT` — text that must be shown to the user.
51    Alert,
52    /// `BADCHARSET` — the requested charset(s) are unsupported; the list of
53    /// charsets the server does support.
54    BadCharset(Vec<&'a str>),
55    /// `CAPABILITY` — the server's capability list.
56    Capability(Vec<&'a str>),
57    /// `PARSE` — the server failed to parse a message's header/MIME structure.
58    Parse,
59    /// `PERMANENTFLAGS` — flags the client can change permanently in the mailbox.
60    PermanentFlags(Vec<&'a str>),
61    /// `READ-ONLY` — the mailbox was selected read-only.
62    ReadOnly,
63    /// `READ-WRITE` — the mailbox was selected read-write.
64    ReadWrite,
65    /// `TRYCREATE` — the target mailbox does not exist; create it and retry.
66    TryCreate,
67    /// `UIDNEXT` — the predicted next UID value for the mailbox.
68    UidNext(u32),
69    /// `UIDVALIDITY` — the mailbox's UID validity value.
70    UidValidity(u32),
71    /// `UNSEEN` — the sequence number of the first unseen message.
72    Unseen(u32),
73    /// Unrecognized response code: `[ATOM]` or `[ATOM SP rest]`.
74    Other(&'a str, Option<&'a str>),
75}
76
77/// An untagged data response (`* …`).
78#[derive(Debug, PartialEq, Eq)]
79pub enum DataResponse<'a> {
80    /// `CAPABILITY` — the server's advertised capabilities.
81    Capability(Vec<&'a str>),
82    /// `LIST` — a mailbox returned by the `LIST` command.
83    List {
84        /// Mailbox attribute flags (e.g. `\Noselect`, `\HasChildren`).
85        flags: Vec<&'a str>,
86        /// Hierarchy delimiter, or `None` for a flat namespace (`NIL`).
87        delimiter: Option<&'a str>,
88        /// The mailbox name.
89        name: &'a str,
90    },
91    /// `LSUB` — a subscribed mailbox returned by the `LSUB` command.
92    Lsub {
93        /// Mailbox attribute flags.
94        flags: Vec<&'a str>,
95        /// Hierarchy delimiter, or `None` (`NIL`).
96        delimiter: Option<&'a str>,
97        /// The mailbox name.
98        name: &'a str,
99    },
100    /// `STATUS` — requested status attributes for a mailbox.
101    Status {
102        /// The mailbox the status items describe.
103        mailbox: &'a str,
104        /// The returned status items.
105        items: Vec<StatusItem>,
106    },
107    /// `SEARCH` — message numbers (or UIDs) matching a search.
108    Search(Vec<u32>),
109    /// `FLAGS` — the flags defined in the selected mailbox.
110    Flags(Vec<&'a str>),
111    /// `EXISTS` — the number of messages in the mailbox.
112    Exists(u32),
113    /// `RECENT` — the number of messages with the `\Recent` flag.
114    Recent(u32),
115    /// `EXPUNGE` — the sequence number of a message that was expunged.
116    Expunge(u32),
117    /// `FETCH` — message data for a single message.
118    Fetch {
119        /// The message sequence number.
120        seq: u32,
121        /// The fetched attributes for this message.
122        attributes: Vec<FetchAttribute<'a>>,
123    },
124    /// Unrecognized untagged data response — the raw line bytes (without CRLF).
125    Other(&'a [u8]),
126}
127
128/// A single item in a `STATUS` data response.
129#[derive(Debug, PartialEq, Eq)]
130pub enum StatusItem {
131    /// `MESSAGES` — total message count.
132    Messages(u32),
133    /// `RECENT` — count of `\Recent` messages.
134    Recent(u32),
135    /// `UIDNEXT` — predicted next UID.
136    UidNext(u32),
137    /// `UIDVALIDITY` — UID validity value.
138    UidValidity(u32),
139    /// `UNSEEN` — count of unseen messages.
140    Unseen(u32),
141    /// RFC 7889 / RFC 9051 — newer attributes we surface but don't decode further.
142    Other(u32),
143}
144
145/// A single attribute returned in a `FETCH` data response.
146#[derive(Debug, PartialEq, Eq)]
147pub enum FetchAttribute<'a> {
148    /// `FLAGS` — the flags set on the message.
149    Flags(Vec<&'a str>),
150    /// `INTERNALDATE` — the server-side receipt timestamp (raw quoted string).
151    InternalDate(&'a str),
152    /// `RFC822.SIZE` — the message size in octets.
153    Rfc822Size(u32),
154    /// Full RFC822 message (legacy alias for `BODY[]`).
155    Rfc822(&'a [u8]),
156    /// `RFC822.HEADER` — the message header (legacy alias for `BODY[HEADER]`).
157    Rfc822Header(&'a [u8]),
158    /// `RFC822.TEXT` — the message body text (legacy alias for `BODY[TEXT]`).
159    Rfc822Text(&'a [u8]),
160    /// Parsed-form ENVELOPE bytes (raw, including outer parens).
161    Envelope(&'a [u8]),
162    /// `BODY` (no section) — non-extensible body structure as raw bytes.
163    Body(&'a [u8]),
164    /// `BODYSTRUCTURE` — extensible body structure as raw bytes.
165    BodyStructure(&'a [u8]),
166    /// `BODY[<section>]<<origin>>` with the data as raw bytes.
167    BodySection {
168        /// The section specifier (e.g. `HEADER`, `1.2.TEXT`), or `None` for `BODY[]`.
169        section: Option<&'a str>,
170        /// The starting octet offset for a partial (`<origin>`) fetch, if any.
171        origin: Option<u32>,
172        /// The section data, or `None` if the server returned `NIL`.
173        data: Option<&'a [u8]>,
174    },
175    /// `UID` — the unique identifier of the message.
176    Uid(u32),
177}
178
179/// A command-continuation request (`+ [code] text`).
180#[derive(Debug, PartialEq, Eq)]
181pub struct ContinueReq<'a> {
182    /// Optional machine-readable response code in `[...]`, if present.
183    pub code: Option<ResponseCode<'a>>,
184    /// Human-readable continuation text.
185    pub text: &'a str,
186}