imap_types/fetch.rs
1//! Fetch-related types.
2
3use std::{
4 fmt::{Display, Formatter},
5 num::NonZeroU32,
6};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::Arbitrary;
10#[cfg(feature = "bounded-static")]
11use bounded_static::ToStatic;
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 body::BodyStructure,
17 core::{AString, NString, NonEmptyVec},
18 datetime::DateTime,
19 envelope::Envelope,
20 flag::FlagFetch,
21};
22
23/// Shorthands for commonly-used message data items.
24#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
25#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28#[non_exhaustive]
29pub enum Macro {
30 /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE)`.
31 Fast,
32 /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)`.
33 All,
34 /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)`.
35 Full,
36}
37
38impl Macro {
39 pub fn expand(&self) -> Vec<MessageDataItemName> {
40 use MessageDataItemName::*;
41
42 match self {
43 Self::All => vec![Flags, InternalDate, Rfc822Size, Envelope],
44 Self::Fast => vec![Flags, InternalDate, Rfc822Size],
45 Self::Full => vec![Flags, InternalDate, Rfc822Size, Envelope, Body],
46 }
47 }
48}
49
50impl Display for Macro {
51 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52 f.write_str(match self {
53 Macro::All => "ALL",
54 Macro::Fast => "FAST",
55 Macro::Full => "FULL",
56 })
57 }
58}
59
60/// Either a macro or a list of message data items.
61///
62/// A macro must be used by itself, and not in conjunction with other macros or data items.
63#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
64#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66#[derive(Debug, Clone, PartialEq, Eq, Hash)]
67pub enum MacroOrMessageDataItemNames<'a> {
68 Macro(Macro),
69 MessageDataItemNames(Vec<MessageDataItemName<'a>>),
70}
71
72impl<'a> From<Macro> for MacroOrMessageDataItemNames<'a> {
73 fn from(m: Macro) -> Self {
74 MacroOrMessageDataItemNames::Macro(m)
75 }
76}
77
78impl<'a> From<Vec<MessageDataItemName<'a>>> for MacroOrMessageDataItemNames<'a> {
79 fn from(item_names: Vec<MessageDataItemName<'a>>) -> Self {
80 MacroOrMessageDataItemNames::MessageDataItemNames(item_names)
81 }
82}
83
84/// Message data item name used to request a message data item.
85#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
86#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88#[derive(Debug, Clone, PartialEq, Eq, Hash)]
89#[doc(alias = "FetchAttribute")]
90pub enum MessageDataItemName<'a> {
91 /// Non-extensible form of `BODYSTRUCTURE`.
92 ///
93 /// ```imap
94 /// BODY
95 /// ```
96 Body,
97
98 /// The text of a particular body section.
99 ///
100 /// ```imap
101 /// BODY[<section>]<<partial>>
102 /// ```
103 BodyExt {
104 /// The section specification is a set of zero or more part specifiers delimited by periods.
105 ///
106 /// An empty section specification refers to the entire message, including the header.
107 ///
108 /// See [`crate::fetch::Section`] and [`crate::fetch::PartSpecifier`].
109 ///
110 /// Every message has at least one part number. Non-[MIME-IMB]
111 /// messages, and non-multipart [MIME-IMB] messages with no
112 /// encapsulated message, only have a part 1.
113 ///
114 /// Multipart messages are assigned consecutive part numbers, as
115 /// they occur in the message. If a particular part is of type
116 /// message or multipart, its parts MUST be indicated by a period
117 /// followed by the part number within that nested multipart part.
118 ///
119 /// A part of type MESSAGE/RFC822 also has nested part numbers,
120 /// referring to parts of the MESSAGE part's body.
121 section: Option<Section<'a>>,
122 /// It is possible to fetch a substring of the designated text.
123 /// This is done by appending an open angle bracket ("<"), the
124 /// octet position of the first desired octet, a period, the
125 /// maximum number of octets desired, and a close angle bracket
126 /// (">") to the part specifier. If the starting octet is beyond
127 /// the end of the text, an empty string is returned.
128 ///
129 /// Any partial fetch that attempts to read beyond the end of the
130 /// text is truncated as appropriate. A partial fetch that starts
131 /// at octet 0 is returned as a partial fetch, even if this
132 /// truncation happened.
133 ///
134 /// Note: This means that BODY[]<0.2048> of a 1500-octet message
135 /// will return BODY[]<0> with a literal of size 1500, not
136 /// BODY[].
137 ///
138 /// Note: A substring fetch of a HEADER.FIELDS or
139 /// HEADER.FIELDS.NOT part specifier is calculated after
140 /// subsetting the header.
141 partial: Option<(u32, NonZeroU32)>,
142 /// Defines, wheather BODY or BODY.PEEK should be used.
143 ///
144 /// `BODY[...]` implicitly sets the `\Seen` flag where `BODY.PEEK[...]` does not.
145 peek: bool,
146 },
147
148 /// The [MIME-IMB] body structure of a message.
149 ///
150 /// This is computed by the server by parsing the [MIME-IMB] header fields in the [RFC-2822]
151 /// header and [MIME-IMB] headers.
152 ///
153 /// ```imap
154 /// BODYSTRUCTURE
155 /// ```
156 BodyStructure,
157
158 /// The envelope structure of a message.
159 ///
160 /// This is computed by the server by parsing the [RFC-2822] header into the component parts,
161 /// defaulting various fields as necessary.
162 ///
163 /// ```imap
164 /// ENVELOPE
165 /// ```
166 Envelope,
167
168 /// The flags that are set for a message.
169 ///
170 /// ```imap
171 /// FLAGS
172 /// ```
173 Flags,
174
175 /// The internal date of a message.
176 ///
177 /// ```imap
178 /// INTERNALDATE
179 /// ```
180 InternalDate,
181
182 /// Functionally equivalent to `BODY[]`.
183 ///
184 /// Differs in the syntax of the resulting untagged FETCH data (`RFC822` is returned).
185 ///
186 /// ```imap
187 /// RFC822
188 /// ```
189 ///
190 /// Note: `BODY[]` is constructed as ...
191 ///
192 /// ```rust
193 /// # use imap_types::fetch::MessageDataItemName;
194 /// MessageDataItemName::BodyExt {
195 /// section: None,
196 /// partial: None,
197 /// peek: false,
198 /// };
199 /// ```
200 Rfc822,
201
202 /// Functionally equivalent to `BODY.PEEK[HEADER]`.
203 ///
204 /// Differs in the syntax of the resulting untagged FETCH data (`RFC822.HEADER` is returned).
205 ///
206 /// ```imap
207 /// RFC822.HEADER
208 /// ```
209 Rfc822Header,
210
211 /// The [RFC-2822] size of a message.
212 ///
213 /// ```imap
214 /// RFC822.SIZE
215 /// ```
216 Rfc822Size,
217
218 /// Functionally equivalent to `BODY[TEXT]`.
219 ///
220 /// Differs in the syntax of the resulting untagged FETCH data (`RFC822.TEXT` is returned).
221 /// ```imap
222 /// RFC822.TEXT
223 /// ```
224 Rfc822Text,
225
226 /// The unique identifier for a message.
227 ///
228 /// ```imap
229 /// UID
230 /// ```
231 Uid,
232}
233
234/// Message data item.
235#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
236#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238#[derive(Debug, Clone, PartialEq, Eq, Hash)]
239#[doc(alias = "FetchAttributeValue")]
240pub enum MessageDataItem<'a> {
241 /// A form of `BODYSTRUCTURE` without extension data.
242 ///
243 /// ```imap
244 /// BODY
245 /// ```
246 Body(BodyStructure<'a>),
247
248 /// The body contents of the specified section.
249 ///
250 /// 8-bit textual data is permitted if a \[CHARSET\] identifier is
251 /// part of the body parameter parenthesized list for this section.
252 /// Note that headers (part specifiers HEADER or MIME, or the
253 /// header portion of a MESSAGE/RFC822 part), MUST be 7-bit; 8-bit
254 /// characters are not permitted in headers. Note also that the
255 /// [RFC-2822] delimiting blank line between the header and the
256 /// body is not affected by header line subsetting; the blank line
257 /// is always included as part of header data, except in the case
258 /// of a message which has no body and no blank line.
259 ///
260 /// Non-textual data such as binary data MUST be transfer encoded
261 /// into a textual form, such as BASE64, prior to being sent to the
262 /// client. To derive the original binary data, the client MUST
263 /// decode the transfer encoded string.
264 ///
265 /// ```imap
266 /// BODY[<section>]<<origin octet>>
267 /// ```
268 BodyExt {
269 /// The specified section.
270 section: Option<Section<'a>>,
271 /// If the origin octet is specified, this string is a substring of
272 /// the entire body contents, starting at that origin octet. This
273 /// means that `BODY[]<0>` MAY be truncated, but `BODY[]` is NEVER
274 /// truncated.
275 ///
276 /// Note: The origin octet facility MUST NOT be used by a server
277 /// in a FETCH response unless the client specifically requested
278 /// it by means of a FETCH of a `BODY[<section>]<<partial>>` data
279 /// item.
280 origin: Option<u32>,
281 /// The string SHOULD be interpreted by the client according to the
282 /// content transfer encoding, body type, and subtype.
283 data: NString<'a>,
284 },
285
286 /// The [MIME-IMB] body structure of a message.
287 ///
288 /// This is computed by the server by parsing the [MIME-IMB] header fields, defaulting various
289 /// fields as necessary.
290 ///
291 /// ```imap
292 /// BODYSTRUCTURE
293 /// ```
294 BodyStructure(BodyStructure<'a>),
295
296 /// The envelope structure of a message.
297 ///
298 /// This is computed by the server by parsing the [RFC-2822] header into the component parts,
299 /// defaulting various fields as necessary.
300 ///
301 /// ```imap
302 /// ENVELOPE
303 /// ```
304 Envelope(Envelope<'a>),
305
306 /// A list of flags that are set for a message.
307 ///
308 /// ```imap
309 /// FLAGS
310 /// ```
311 Flags(Vec<FlagFetch<'a>>),
312
313 /// A string representing the internal date of a message.
314 ///
315 /// ```imap
316 /// INTERNALDATE
317 /// ```
318 InternalDate(DateTime),
319
320 /// Equivalent to `BODY[]`.
321 ///
322 /// ```imap
323 /// RFC822
324 /// ```
325 Rfc822(NString<'a>),
326
327 /// Equivalent to `BODY[HEADER]`.
328 ///
329 /// Note that this did not result in `\Seen` being set, because `RFC822.HEADER` response data
330 /// occurs as a result of a `FETCH` of `RFC822.HEADER`. `BODY[HEADER]` response data occurs as a
331 /// result of a `FETCH` of `BODY[HEADER]` (which sets `\Seen`) or `BODY.PEEK[HEADER]` (which
332 /// does not set `\Seen`).
333 ///
334 /// ```imap
335 /// RFC822.HEADER
336 /// ```
337 Rfc822Header(NString<'a>),
338
339 /// A number expressing the [RFC-2822] size of a message.
340 ///
341 /// ```imap
342 /// RFC822.SIZE
343 /// ```
344 Rfc822Size(u32),
345
346 /// Equivalent to `BODY[TEXT]`.
347 ///
348 /// ```imap
349 /// RFC822.TEXT
350 /// ```
351 Rfc822Text(NString<'a>),
352
353 /// A number expressing the unique identifier of a message.
354 ///
355 /// ```imap
356 /// UID
357 /// ```
358 Uid(NonZeroU32),
359}
360
361/// A part specifier is either a part number or one of the following:
362/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
363///
364/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
365/// specifiers refer to the [RFC-2822] header of the message or of
366/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
367/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
368/// field-name (as defined in [RFC-2822]) names, and return a
369/// subset of the header.
370///
371/// The field-matching is case-insensitive but otherwise exact.
372/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
373/// and the body; the blank line is included in all header fetches,
374/// except in the case of a message which has no body and no blank
375/// line.
376///
377/// The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part
378/// specifiers can be the sole part specifier or can be prefixed by
379/// one or more numeric part specifiers, provided that the numeric
380/// part specifier refers to a part of type MESSAGE/RFC822.
381///
382/// Here is an example of a complex message with some of its part specifiers:
383///
384/// ```text
385/// HEADER ([RFC-2822] header of the message)
386/// TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
387/// 1 TEXT/PLAIN
388/// 2 APPLICATION/OCTET-STREAM
389/// 3 MESSAGE/RFC822
390/// 3.HEADER ([RFC-2822] header of the message)
391/// 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
392/// 3.1 TEXT/PLAIN
393/// 3.2 APPLICATION/OCTET-STREAM
394/// 4 MULTIPART/MIXED
395/// 4.1 IMAGE/GIF
396/// 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF)
397/// 4.2 MESSAGE/RFC822
398/// 4.2.HEADER ([RFC-2822] header of the message)
399/// 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
400/// 4.2.1 TEXT/PLAIN
401/// 4.2.2 MULTIPART/ALTERNATIVE
402/// 4.2.2.1 TEXT/PLAIN
403/// 4.2.2.2 TEXT/RICHTEXT
404/// ```
405#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
406#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
407#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
408#[derive(Debug, Clone, PartialEq, Eq, Hash)]
409pub enum Section<'a> {
410 Part(Part),
411
412 Header(Option<Part>),
413
414 /// The subset returned by HEADER.FIELDS contains only those header fields with a field-name that
415 /// matches one of the names in the list.
416 HeaderFields(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
417
418 /// Similarly, the subset returned by HEADER.FIELDS.NOT contains only the header fields
419 /// with a non-matching field-name.
420 HeaderFieldsNot(Option<Part>, NonEmptyVec<AString<'a>>), // TODO: what if none matches?
421
422 /// The TEXT part specifier refers to the text body of the message, omitting the [RFC-2822] header.
423 Text(Option<Part>),
424
425 /// The MIME part specifier MUST be prefixed by one or more numeric part specifiers
426 /// and refers to the [MIME-IMB] header for this part.
427 Mime(Part),
428}
429
430#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
431#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
432#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
433#[derive(Debug, Clone, PartialEq, Eq, Hash)]
434pub struct Part(pub NonEmptyVec<NonZeroU32>);
435
436/// A part specifier is either a part number or one of the following:
437/// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`.
438///
439/// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
440/// specifiers refer to the [RFC-2822] header of the message or of
441/// an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
442/// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
443/// field-name (as defined in [RFC-2822]) names, and return a
444/// subset of the header.
445///
446/// The field-matching is case-insensitive but otherwise exact.
447/// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header
448/// and the body; the blank line is included in all header fetches,
449/// except in the case of a message which has no body and no blank
450/// line.
451#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
452#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
453#[derive(Debug, Clone, PartialEq, Eq, Hash)]
454pub enum PartSpecifier<'a> {
455 PartNumber(u32),
456 Header,
457 HeaderFields(NonEmptyVec<AString<'a>>),
458 HeaderFieldsNot(NonEmptyVec<AString<'a>>),
459 Mime,
460 Text,
461}