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}