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}