Skip to main content

mail_builder/headers/
content_type.rs

1/*
2 * Copyright Stalwart Labs Ltd. See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use std::borrow::Cow;
13
14use super::Header;
15use crate::encoders::encode::rfc2047_encode;
16
17/// MIME Content-Type or Content-Disposition header
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19pub struct ContentType<'x> {
20    pub c_type: Cow<'x, str>,
21    pub attributes: Vec<(Cow<'x, str>, Cow<'x, str>)>,
22}
23
24impl<'x> ContentType<'x> {
25    /// Create a new Content-Type or Content-Disposition header
26    pub fn new(c_type: impl Into<Cow<'x, str>>) -> Self {
27        Self {
28            c_type: c_type.into(),
29            attributes: Vec::new(),
30        }
31    }
32
33    /// Set a Content-Type / Content-Disposition attribute
34    pub fn attribute(mut self, key: impl Into<Cow<'x, str>>, value: impl Into<Cow<'x, str>>) -> Self {
35        self.attributes.push((key.into(), value.into()));
36        self
37    }
38
39    /// Returns true when the part is text/*
40    pub fn is_text(&self) -> bool {
41        self.c_type.starts_with("text/")
42    }
43
44    /// Returns true when the part is an attachment
45    pub fn is_attachment(&self) -> bool {
46        self.c_type == "attachment"
47    }
48}
49
50impl<'x> Header for ContentType<'x> {
51    fn write_header(&self, mut output: impl std::io::Write, mut bytes_written: usize) -> std::io::Result<usize> {
52        output.write_all(self.c_type.as_bytes())?;
53        bytes_written += self.c_type.len();
54        if !self.attributes.is_empty() {
55            output.write_all(b"; ")?;
56            bytes_written += 2;
57            for (pos, (key, value)) in self.attributes.iter().enumerate() {
58                if bytes_written + key.len() + value.len() + 3 >= 76 {
59                    output.write_all(b"\r\n\t")?;
60                    bytes_written = 1;
61                }
62
63                output.write_all(key.as_bytes())?;
64                output.write_all(b"=")?;
65                bytes_written += rfc2047_encode(value, &mut output)? + key.len() + 1;
66                if pos < self.attributes.len() - 1 {
67                    output.write_all(b"; ")?;
68                    bytes_written += 2;
69                }
70            }
71        }
72        output.write_all(b"\r\n")?;
73        Ok(0)
74    }
75}