Skip to main content

mail_builder/headers/
content_type.rs

1use std::borrow::Cow;
2
3use super::{
4    Header,
5    fold::{FoldWriter, write_control_free},
6    rfc2047::write_parameter,
7};
8use crate::writer::Writer;
9
10/// MIME Content-Type or Content-Disposition header
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct ContentType<'x> {
13    pub c_type: Cow<'x, str>,
14    pub attributes: Vec<(Cow<'x, str>, Cow<'x, str>)>,
15}
16
17impl<'x> ContentType<'x> {
18    /// Create a new Content-Type or Content-Disposition header
19    pub fn new(c_type: impl Into<Cow<'x, str>>) -> Self {
20        Self {
21            c_type: c_type.into(),
22            attributes: Vec::new(),
23        }
24    }
25
26    /// Set a Content-Type / Content-Disposition attribute
27    pub fn attribute(mut self, key: impl Into<Cow<'x, str>>, value: impl Into<Cow<'x, str>>) -> Self {
28        self.attributes.push((key.into(), value.into()));
29        self
30    }
31
32    /// Returns true when the part is text/*
33    pub fn is_text(&self) -> bool {
34        self.c_type
35            .get(..5)
36            .is_some_and(|prefix| prefix.eq_ignore_ascii_case("text/"))
37    }
38
39    /// Returns true when the part is an attachment
40    pub fn is_attachment(&self) -> bool {
41        self.c_type.eq_ignore_ascii_case("attachment")
42    }
43}
44
45impl Header for ContentType<'_> {
46    fn write_header(&self, output: &mut impl Writer, column: usize) {
47        let mut folder = FoldWriter::new(output, column);
48        write_control_free(&mut folder, self.c_type.as_bytes());
49
50        if let Some((last, head)) = self.attributes.split_last() {
51            for (key, value) in head {
52                folder.semicolon();
53                write_parameter(&mut folder, key, value, 1);
54            }
55            folder.semicolon();
56            write_parameter(&mut folder, &last.0, &last.1, 0);
57        }
58
59        folder.finish();
60    }
61}