1use std::{fmt, str::FromStr};
2use thiserror::Error;
3
4#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
5pub struct EmailAddress {
7 address: String,
8 display_name: Option<String>,
9}
10
11impl EmailAddress {
12 pub const fn new_unchecked(address: String) -> Self {
13 Self {
14 address,
15 display_name: None,
16 }
17 }
18 pub fn into_string(self) -> String {
19 self.address
20 }
21 pub fn as_str(&self) -> &str {
22 self.address.as_str()
23 }
24}
25
26#[derive(Debug, Error)]
27#[error("Invalid E-Mail address")]
28pub struct EmailAddressParseError;
29
30impl FromStr for EmailAddress {
31 type Err = EmailAddressParseError;
32 fn from_str(s: &str) -> Result<EmailAddress, Self::Err> {
33 let info = mailparse::addrparse(s)
34 .ok()
35 .and_then(|list| list.extract_single_info())
36 .ok_or(EmailAddressParseError)?;
37 Ok(Self {
38 address: info.addr,
39 display_name: info.display_name,
40 })
41 }
42}
43
44impl fmt::Display for EmailAddress {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 let EmailAddress {
47 address,
48 display_name,
49 } = self;
50 if let Some(display_name) = &display_name {
51 write!(
52 f,
53 r#""{display_name}" <{address}>"#,
54 display_name = display_name.replace('"', r#"\""#)
55 )
56 } else {
57 write!(f, "{address}")
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
63pub struct EmailContent {
64 pub subject: String,
65 pub body: String,
66}
67
68#[derive(Debug, Clone)]
69pub struct Email {
70 pub to: Vec<EmailAddress>,
71 pub content: EmailContent,
72}