mail_builder/headers/
message_id.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use std::borrow::Cow;
8
9use crate::mime::make_boundary;
10
11use super::Header;
12
13/// RFC5322 Message ID header
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
15pub struct MessageId<'x> {
16    pub id: Vec<Cow<'x, str>>,
17}
18
19impl<'x> MessageId<'x> {
20    /// Create a new Message ID header
21    pub fn new(id: impl Into<Cow<'x, str>>) -> Self {
22        Self {
23            id: vec![id.into()],
24        }
25    }
26
27    /// Create a new multi-value Message ID header
28    pub fn new_list<T, U>(ids: T) -> Self
29    where
30        T: Iterator<Item = U>,
31        U: Into<Cow<'x, str>>,
32    {
33        Self {
34            id: ids.map(|s| s.into()).collect(),
35        }
36    }
37}
38
39impl<'x> From<&'x str> for MessageId<'x> {
40    fn from(value: &'x str) -> Self {
41        Self::new(value)
42    }
43}
44
45impl From<String> for MessageId<'_> {
46    fn from(value: String) -> Self {
47        Self::new(value)
48    }
49}
50
51impl<'x> From<&[&'x str]> for MessageId<'x> {
52    fn from(value: &[&'x str]) -> Self {
53        MessageId {
54            id: value.iter().map(|&s| s.into()).collect(),
55        }
56    }
57}
58
59impl<'x> From<&'x [String]> for MessageId<'x> {
60    fn from(value: &'x [String]) -> Self {
61        MessageId {
62            id: value.iter().map(|s| s.into()).collect(),
63        }
64    }
65}
66
67impl<'x, T> From<Vec<T>> for MessageId<'x>
68where
69    T: Into<Cow<'x, str>>,
70{
71    fn from(value: Vec<T>) -> Self {
72        MessageId {
73            id: value.into_iter().map(|s| s.into()).collect(),
74        }
75    }
76}
77
78pub fn generate_message_id_header(
79    mut output: impl std::io::Write,
80    hostname: &str,
81) -> std::io::Result<()> {
82    output.write_all(b"<")?;
83    output.write_all(make_boundary(".").as_bytes())?;
84    output.write_all(b"@")?;
85    output.write_all(hostname.as_bytes())?;
86    output.write_all(b">")
87}
88
89impl Header for MessageId<'_> {
90    fn write_header(
91        &self,
92        mut output: impl std::io::Write,
93        mut bytes_written: usize,
94    ) -> std::io::Result<usize> {
95        for (pos, id) in self.id.iter().enumerate() {
96            if pos > 0 {
97                if bytes_written + id.len() + 2 >= 76 {
98                    output.write_all(b"\r\n\t")?;
99                    bytes_written = 1;
100                } else {
101                    output.write_all(b" ")?;
102                    bytes_written += 1;
103                }
104            }
105
106            output.write_all(b"<")?;
107            output.write_all(id.as_bytes())?;
108            output.write_all(b">")?;
109            bytes_written += id.len() + 2;
110        }
111
112        if bytes_written > 0 {
113            output.write_all(b"\r\n")?;
114        }
115
116        Ok(0)
117    }
118}