imap_types/
body.rs

1//! Body(structure)-related types.
2
3#[cfg(feature = "arbitrary")]
4use arbitrary::Arbitrary;
5#[cfg(feature = "bounded-static")]
6use bounded_static::ToStatic;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    core::{IString, NString, NonEmptyVec},
12    envelope::Envelope,
13};
14
15/// Inner part of [`BodyStructure`].
16#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
17#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct Body<'a> {
21    /// Basic fields
22    pub basic: BasicFields<'a>,
23    /// Type-specific fields
24    pub specific: SpecificFields<'a>,
25}
26
27/// Basic fields of a non-multipart body part.
28#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
29#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31#[derive(Debug, Clone, PartialEq, Eq, Hash)]
32pub struct BasicFields<'a> {
33    /// List of attribute/value pairs ([MIME-IMB].)
34    pub parameter_list: Vec<(IString<'a>, IString<'a>)>,
35
36    /// Content id ([MIME-IMB].)
37    pub id: NString<'a>,
38
39    /// Content description ([MIME-IMB].)
40    pub description: NString<'a>,
41
42    /// Content transfer encoding ([MIME-IMB].)
43    pub content_transfer_encoding: IString<'a>,
44
45    /// Size of the body in octets.
46    ///
47    /// Note that this size is the size in its transfer encoding
48    /// and not the resulting size after any decoding.
49    pub size: u32,
50}
51
52/// Specific fields of a non-multipart body part.
53#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
54#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
55#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub enum SpecificFields<'a> {
58    /// # Example (not in RFC)
59    ///
60    /// Single application/{voodoo, unknown, whatever, meh} is represented as "basic"
61    ///
62    /// ```text
63    /// (
64    ///     "application" "voodoo" NIL NIL NIL "7bit" 20
65    ///                            ^^^ ^^^ ^^^ ^^^^^^ ^^
66    ///                            |   |   |   |      | size
67    ///                            |   |   |   | content transfer encoding
68    ///                            |   |   | description
69    ///                            |   | id
70    ///                            | parameter list
71    ///
72    ///     NIL NIL NIL NIL
73    ///     ^^^ ^^^ ^^^ ^^^
74    ///     |   |   |   | location
75    ///     |   |   | language
76    ///     |   | disposition
77    ///     | md5
78    /// )
79    /// ```
80    Basic {
81        /// A string giving the content media type name as defined in [MIME-IMB].
82        r#type: IString<'a>,
83
84        /// A string giving the content subtype name as defined in [MIME-IMB].
85        subtype: IString<'a>,
86    },
87
88    /// # Example (not in RFC)
89    ///
90    /// Single message/rfc822 is represented as "message"
91    ///
92    /// ```text
93    /// (
94    ///     "message" "rfc822" NIL NIL NIL "7bit" 123
95    ///                        ^^^ ^^^ ^^^ ^^^^^^ ^^^
96    ///                        |   |   |   |      | size
97    ///                        |   |   |   | content transfer encoding
98    ///                        |   |   | description
99    ///                        |   | id
100    ///                        | parameter list
101    ///
102    ///     # envelope
103    ///     (
104    ///         NIL "message.inner.subject.ljcwooqy" ((NIL NIL "extern" "company.com")) ((NIL NIL "extern" "company.com")) ((NIL NIL "extern" "company.com")) ((NIL NIL "admin" "seurity.com")) NIL NIL NIL NIL
105    ///     )
106    ///
107    ///     # body structure
108    ///     (
109    ///         "text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 31
110    ///         2
111    ///         NIL NIL NIL NIL
112    ///     )
113    ///
114    ///     6
115    ///     ^
116    ///     | number of lines
117    ///
118    ///     NIL NIL NIL NIL
119    ///     ^^^ ^^^ ^^^ ^^^
120    ///     |   |   |   | location
121    ///     |   |   | language
122    ///     |   | disposition
123    ///     | md5
124    /// )
125    /// ```
126    ///
127    /// A body type of type MESSAGE and subtype RFC822 contains, immediately after the basic fields,
128    Message {
129        /// the envelope structure,
130        envelope: Box<Envelope<'a>>,
131        /// body structure,
132        body_structure: Box<BodyStructure<'a>>,
133        /// and size in text lines of the encapsulated message.
134        number_of_lines: u32,
135    },
136
137    /// # Example (not in RFC)
138    ///
139    /// Single text/plain is represented as "text"
140    ///
141    /// ```text
142    /// (
143    ///     "text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 25
144    ///                    ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^
145    ///                    |                      |   |   |      | size
146    ///                    |                      |   |   | content transfer encoding
147    ///                    |                      |   | description
148    ///                    |                      | id
149    ///                    | parameter list
150    ///
151    ///     1
152    ///     ^
153    ///     | number of lines
154    ///
155    ///     NIL NIL NIL NIL
156    ///     ^^^ ^^^ ^^^ ^^^
157    ///     |   |   |   | location
158    ///     |   |   | language
159    ///     |   | disposition
160    ///     | md5
161    /// )
162    /// ```
163    Text {
164        /// Subtype.
165        subtype: IString<'a>,
166        /// Size of the body in text lines.
167        number_of_lines: u32,
168    },
169}
170
171/// The BODY(STRUCTURE).
172#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
173#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
174#[derive(Debug, Clone, PartialEq, Eq, Hash)]
175pub enum BodyStructure<'a> {
176    /// For example, a simple text message of 48 lines and 2279 octets
177    /// can have a body structure of:
178    ///
179    /// ```text
180    /// ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279 48)
181    /// ```
182    Single {
183        /// Inner body.
184        body: Body<'a>,
185        /// Extension data
186        ///
187        /// Extension data is never returned with the BODY fetch,
188        /// but can be returned with a BODYSTRUCTURE fetch.
189        /// Extension data, if present, MUST be in the defined order.
190        ///
191        /// Any following extension data are not yet defined in this
192        /// version of the protocol, and would be as described above under
193        /// multipart extension data.
194        extension_data: Option<SinglePartExtensionData<'a>>,
195    },
196
197    /// Multiple parts are indicated by parenthesis nesting.  Instead
198    /// of a body type as the first element of the parenthesized list,
199    /// there is a sequence of one or more nested body structures.  The
200    /// second (last?!) element of the parenthesized list is the multipart
201    /// subtype (mixed, digest, parallel, alternative, etc.).
202    ///
203    /// For example, a two part message consisting of a text and a
204    /// BASE64-encoded text attachment can have a body structure of:
205    ///
206    /// ```text
207    /// (
208    ///     ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)
209    ///     ("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") "<960723163407.20117h@cac.washington.edu>" "Compiler diff" "BASE64" 4554 73)
210    ///     "MIXED"
211    /// )
212    /// ```
213    ///
214    /// Extension data follows the multipart subtype.  Extension data
215    /// is never returned with the BODY fetch, but can be returned with
216    /// a BODYSTRUCTURE fetch.  Extension data, if present, MUST be in
217    /// the defined order.
218    ///
219    /// See [ExtensionMultiPartData](struct.ExtensionMultiPartData.html).
220    ///
221    /// Any following extension data are not yet defined in this
222    /// version of the protocol.  Such extension data can consist of
223    /// zero or more NILs, strings, numbers, or potentially nested
224    /// parenthesized lists of such data.  Client implementations that
225    /// do a BODYSTRUCTURE fetch MUST be prepared to accept such
226    /// extension data.  Server implementations MUST NOT send such
227    /// extension data until it has been defined by a revision of this
228    /// protocol.
229    ///
230    /// # Example (not in RFC)
231    ///
232    /// Multipart/mixed is represented as follows...
233    ///
234    /// ```text
235    /// (
236    ///     ("text" "html" ("charset" "us-ascii") NIL NIL "7bit" 28 0 NIL NIL NIL NIL)
237    ///     ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 11 0 NIL NIL NIL NIL)
238    ///     "mixed" ("boundary" "xxx") NIL NIL NIL
239    ///             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
240    ///             |
241    ///             | extension data
242    /// )
243    /// ```
244    Multi {
245        /// Inner bodies.
246        bodies: NonEmptyVec<BodyStructure<'a>>,
247        /// Subtype.
248        subtype: IString<'a>,
249        /// Extension data.
250        extension_data: Option<MultiPartExtensionData<'a>>,
251    },
252}
253
254/// The extension data of a non-multipart body part.
255#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
256#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
257#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
258#[derive(Debug, Clone, PartialEq, Eq, Hash)]
259pub struct SinglePartExtensionData<'a> {
260    /// A string giving the body MD5 value as defined in \[MD5\].
261    pub md5: NString<'a>,
262
263    /// (Optional) additional data.
264    pub tail: Option<Disposition<'a>>,
265}
266
267/// The extension data of a multipart body part.
268///
269/// # Trace (not in RFC)
270///
271/// ```text
272/// (
273///   ("text" "html"  ("charset" "us-ascii") NIL NIL "7bit" 28 0 NIL NIL NIL NIL)
274///   ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 11 0 NIL NIL NIL NIL)
275///   "mixed" ("boundary" "xxx") NIL NIL NIL
276///           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277///           |
278///           | extension multipart data
279/// )
280/// ```
281#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
282#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
283#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
284#[derive(Debug, Clone, PartialEq, Eq, Hash)]
285pub struct MultiPartExtensionData<'a> {
286    /// A parenthesized list of attribute/value pairs [e.g., ("foo"
287    /// "bar" "baz" "rag") where "bar" is the value of "foo", and
288    /// "rag" is the value of "baz"] as defined in [MIME-IMB].
289    pub parameter_list: Vec<(IString<'a>, IString<'a>)>,
290
291    /// (Optional) additional data.
292    pub tail: Option<Disposition<'a>>,
293}
294
295/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
296#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
297#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
300pub struct Disposition<'a> {
301    /// A parenthesized list, consisting of a disposition type
302    /// string, followed by a parenthesized list of disposition
303    /// attribute/value pairs as defined in \[DISPOSITION\].
304    pub disposition: Option<(IString<'a>, Vec<(IString<'a>, IString<'a>)>)>,
305
306    /// (Optional) additional data.
307    pub tail: Option<Language<'a>>,
308}
309
310/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
311#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
312#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
313#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
314#[derive(Debug, Clone, PartialEq, Eq, Hash)]
315pub struct Language<'a> {
316    /// A string or parenthesized list giving the body language
317    /// value as defined in [LANGUAGE-TAGS].
318    pub language: Vec<IString<'a>>,
319
320    /// (Optional) additional data.
321    pub tail: Option<Location<'a>>,
322}
323
324/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
325#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
326#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
327#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
328#[derive(Debug, Clone, PartialEq, Eq, Hash)]
329pub struct Location<'a> {
330    /// A string list giving the body content URI as defined in \[LOCATION\].
331    pub location: NString<'a>,
332
333    /// Extension data.
334    pub extensions: Vec<BodyExtension<'a>>,
335}
336
337/// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`].
338#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
339#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
340#[derive(Debug, Clone, PartialEq, Eq, Hash)]
341pub enum BodyExtension<'a> {
342    /// NString.
343    NString(NString<'a>),
344    /// Number.
345    Number(u32),
346    /// List.
347    List(NonEmptyVec<BodyExtension<'a>>),
348}