Skip to main content

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