mail_core/resource/
data.rs

1use std::{
2    sync::Arc,
3    default::Default,
4    ops::{Deref, DerefMut}
5};
6
7#[cfg(feature="serde")]
8use serde::{
9    Serialize, Deserialize,
10    ser::{Serializer},
11    de::{Deserializer}
12};
13
14use internals::bind::{base64, quoted_printable};
15use headers::header_components::{
16    MediaType,
17    FileMeta,
18    TransferEncoding,
19    ContentId
20};
21
22
23
24/// POD type containing FileMeta, Content-Type and Content-Id
25///
26/// The file meta contains optional information like file name and read
27/// as well as last modification data.
28///
29/// The media type will be used for the content type header which is used
30/// to determine how a mail client will handle the file. It is also used
31/// to get a hint on how to best transfer encode the file.
32///
33/// The content id is used to identify the "data" and refer to it
34/// from some other place. For example in a mail the html body could
35/// refer to a image contained in the mail to embed it in the mail.
36///
37/// As Content-Id's are supposed to be world unique they could also
38/// be used for some caching and similar but that plays hardly any
39/// role any more, except maybe for "external" mail bodies.
40#[derive(Debug, Clone)]
41#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
42pub struct Metadata {
43    /// File meta like file name or file read time.
44    #[cfg_attr(feature="serde", serde(flatten))]
45    pub file_meta: FileMeta,
46
47    /// The media type of the data.
48    pub media_type: MediaType,
49
50    /// The content id associated with the data.
51    pub content_id: ContentId
52}
53
54impl Deref for Metadata {
55    type Target = FileMeta;
56
57    fn deref(&self) -> &Self::Target {
58        &self.file_meta
59    }
60}
61
62impl DerefMut for Metadata {
63    fn deref_mut(&mut self) -> &mut Self::Target {
64        &mut self.file_meta
65    }
66}
67
68/// A type containing some data and metadata for it.
69///
70/// This often, but not always, corresponds to data which could potentially
71/// have been a file in a file system. For example a image or a text
72/// document.
73///
74/// This type is mainly used when having auto generated content as content
75/// provided through a file should be loaded from a source and as such
76/// will be directly loaded and transfer encoded.
77///
78/// # Clone
79///
80/// `Data` is made to be cheap to clone and share.
81/// For this it uses `Arc` internally.
82#[derive(Debug, Clone)]
83#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
84pub struct Data {
85    #[cfg_attr(feature="serde", serde(with="arc_buffer_serde"))]
86    buffer: Arc<[u8]>,
87    #[cfg_attr(feature="serde", serde(flatten))]
88    #[cfg_attr(feature="serde", serde(with="arc_serde"))]
89    meta: Arc<Metadata>
90}
91
92
93impl Data {
94
95    /// Create a new data instance.
96    pub fn new(
97        buffer: impl Into<Arc<[u8]>>,
98        meta: impl Into<Arc<Metadata>>
99    ) -> Self {
100        Data {
101            buffer: buffer.into(),
102            meta: meta.into()
103        }
104    }
105
106    pub fn plain_text(text: impl Into<String>, cid: ContentId) -> Data {
107        let text = text.into();
108        let buf = text.into_bytes();
109        let meta = Metadata {
110            file_meta: Default::default(),
111            media_type: MediaType::parse("text/plain; charset=utf-8").unwrap(),
112            content_id: cid
113        };
114        Self::new(buf, meta)
115    }
116
117    /// Access the raw data buffer of this instance.
118    pub fn buffer(&self) -> &Arc<[u8]> {
119        &self.buffer
120    }
121
122    /// Access the metadata.
123    pub fn metadata(&self) -> &Arc<Metadata> {
124        &self.meta
125    }
126
127    /// Access the file meta metadata.Fn
128    pub fn file_meta(&self) -> &FileMeta {
129        &self.meta.file_meta
130    }
131
132    /// Access the content type.
133    pub fn media_type(&self) -> &MediaType {
134        &self.meta.media_type
135    }
136
137    /// Access the content id.
138    pub fn content_id(&self) -> &ContentId {
139        &self.meta.content_id
140    }
141
142    /// Transfer encode the given data.
143    ///
144    /// This function will be called by the context implementation when
145    /// loading and/or transfer encoding data. The context implementation
146    /// might also not call it if it has a cached version of the transfer
147    /// encoded data.
148    ///
149    /// This functions expect a boundary pool and will remove all boundaries
150    /// which do appear in the encoded representation of the data.
151    #[inline(always)]
152    pub fn transfer_encode(
153        &self,
154        encoding_hint: TransferEncodingHint,
155    ) -> EncData {
156        // delegated to free function at end of file for
157        // readability
158        transfer_encode(self, encoding_hint)
159    }
160}
161
162/// `EncData` is like `Data` but the buffer contains transfer encoded data.
163///
164/// # Clone
165///
166/// `Data` is made to be cheap to clone and share.
167/// For this it uses `Arc` internally.
168#[derive(Debug, Clone)]
169#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
170pub struct EncData {
171    #[cfg_attr(feature="serde", serde(with="arc_buffer_serde"))]
172    buffer: Arc<[u8]>,
173    #[cfg_attr(feature="serde", serde(flatten))]
174    #[cfg_attr(feature="serde", serde(with="arc_serde"))]
175    meta: Arc<Metadata>,
176    encoding: TransferEncoding
177}
178
179impl EncData {
180
181    /// Create a new instance from transfer encoded data
182    /// as well as metadata and the encoding used to transfer
183    /// encode the data.
184    ///
185    /// If the `buffer` was created by transfer encoding data
186    /// from a `Data` instance the `Arc<Metadata>` from that
187    /// `Data` instance can be passed in directly as `meta`.
188    pub(crate) fn new(
189        buffer: impl Into<Arc<[u8]>>,
190        meta: impl Into<Arc<Metadata>>,
191        encoding: TransferEncoding
192    ) -> Self {
193        EncData {
194            buffer: buffer.into(),
195            meta: meta.into(),
196            encoding
197        }
198    }
199
200    /// Access the raw transfer encoded data.
201    pub fn transfer_encoded_buffer(&self) -> &Arc<[u8]> {
202        &self.buffer
203    }
204
205    /// Access the metadata.
206    pub fn metadata(&self) -> &Arc<Metadata> {
207        &self.meta
208    }
209
210    /// Access the file meta metadata.Fn
211    pub fn file_meta(&self) -> &FileMeta {
212        &self.meta.file_meta
213    }
214
215    /// Access the content type.
216    pub fn media_type(&self) -> &MediaType {
217        &self.meta.media_type
218    }
219
220
221    /// Access the transfer encoding used to encode the buffer.
222    pub fn encoding(&self) -> TransferEncoding {
223        self.encoding
224    }
225
226    /// Access the content id.
227    ///
228    /// The content id is for the data itself so it should not
229    /// change just because the data had been transfer encoded.
230    ///
231    /// # Note about fixed newlines:
232    ///
233    /// The encoding functions of this library will always "fix"
234    /// line endings even if the transfer encoding is to not have
235    /// any encoding, it could be said that this is a modification
236    /// of the data and as such the content id should change. But
237    /// as this is done _always_ and as such only the transfer encoded
238    /// data is "send" out this works out fine.
239    pub fn content_id(&self) -> &ContentId {
240        &self.meta.content_id
241    }
242}
243
244/// Hint to change how data should be transfer encoded.
245#[derive(Debug, PartialEq)]
246#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
247pub enum TransferEncodingHint {
248    /// Use Base64 encoding.
249    UseBase64,
250
251    /// Use Quoted-Printable encoding.
252    UseQuotedPrintable,
253
254    // /// Do not assume Mime8Bit is available.
255    // ///
256    // /// As such do not encode ascii/utf-8 "as is" (e.g. not encoding them).
257    // ///
258    // /// Note: This is the default until I'm more sure about the whole thing
259    // /// with puthing things in unecoded.
260    // DoNotUseNoEncoding,
261
262    /// No hint for transfer encoding.
263    NoHint,
264
265    #[cfg_attr(feature="serde", serde(skip))]
266    #[doc(hidden)]
267    __NonExhaustive { }
268}
269
270impl Default for TransferEncodingHint {
271    fn default() -> Self {
272        TransferEncodingHint::UseBase64
273    }
274}
275
276/// Transfer encodes Data.
277///
278/// Util we have a reasonable "non latin letter text" heuristic
279/// or enable none encoded text as default this will always encode
280/// with `Base64` except if asked not to do so.
281///
282/// # Panic
283///
284/// Panics if TransferEncodingHint::__NonExhaustive
285/// is passed to the function.
286fn transfer_encode(
287    data: &Data,
288    encoding_hint: TransferEncodingHint,
289) -> EncData {
290    use self::TransferEncodingHint::*;
291
292    match encoding_hint {
293        UseQuotedPrintable => tenc_quoted_printable(data),
294        UseBase64 | NoHint => tenc_base64(data),
295        __NonExhaustive { .. } => panic!("__NonExhaustive encoding should not be passed to any place")
296    }
297}
298
299fn tenc_base64(data: &Data) -> EncData {
300    let enc_data = base64::normal_encode(data.buffer())
301        .into_bytes();
302
303    EncData::new(enc_data, data.metadata().clone(),
304        TransferEncoding::Base64)
305}
306
307fn tenc_quoted_printable(data: &Data) -> EncData {
308    let enc_data = quoted_printable::normal_encode(data.buffer())
309        .into_bytes();
310
311    EncData::new(enc_data, data.metadata().clone(),
312        TransferEncoding::QuotedPrintable)
313}
314
315
316#[cfg(feature="serde")]
317mod arc_buffer_serde {
318    use super::*;
319
320    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Arc<[u8]>, D::Error>
321        where D: Deserializer<'de>
322    {
323        let bytes = <Vec<u8>>::deserialize(deserializer)?;
324        Ok(bytes.into())
325    }
326
327    pub(crate) fn serialize<S>(data: &Arc<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
328        where S: Serializer
329    {
330        serializer.serialize_bytes(data)
331    }
332}
333
334#[cfg(feature="serde")]
335mod arc_serde {
336    use super::*;
337
338    pub(crate) fn deserialize<'de, OUT, D>(deserializer: D) -> Result<Arc<OUT>, D::Error>
339        where D: Deserializer<'de>, OUT: Deserialize<'de>
340    {
341        let value = OUT::deserialize(deserializer)?;
342        Ok(Arc::new(value))
343    }
344
345    pub(crate) fn serialize<S, IN>(data: &Arc<IN>, serializer: S) -> Result<S::Ok, S::Error>
346        where S: Serializer, IN: Serialize
347    {
348        IN::serialize(&**data, serializer)
349    }
350}