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}