Skip to main content

im_core/email/
dto.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
4pub struct EmailMessageId(String);
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub struct EmailFolder(String);
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct EmailAddress(String);
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct EmailInboxQuery {
14    pub folder: EmailFolder,
15    pub limit: crate::ids::PageLimit,
16    pub offset: u32,
17    pub unread_only: bool,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct EmailMarkReadRequest {
22    pub message_ids: Vec<EmailMessageId>,
23    pub is_read: bool,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct SendEmailRequest {
28    pub to: Vec<EmailAddress>,
29    pub cc: Vec<EmailAddress>,
30    pub subject: String,
31    pub body_text: String,
32    pub body_html: Option<String>,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct EmailAttachmentDownloadRequest {
37    pub message_id: EmailMessageId,
38    pub attachment_index: u32,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct EmailNotificationQuery {
43    pub limit: crate::ids::PageLimit,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct EmailAccount {
48    pub mailbox_address: Option<EmailAddress>,
49    pub display_name: Option<String>,
50    pub status: Option<String>,
51    pub attributes: Vec<EmailAttribute>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct EmailMessageSummary {
56    pub id: EmailMessageId,
57    pub folder: Option<EmailFolder>,
58    pub from: Vec<EmailAddress>,
59    pub to: Vec<EmailAddress>,
60    pub cc: Vec<EmailAddress>,
61    pub subject: String,
62    pub preview: Option<String>,
63    pub received_at: Option<String>,
64    pub sent_at: Option<String>,
65    pub unread: bool,
66    pub has_attachments: bool,
67    pub attachment_count: Option<u32>,
68    pub attributes: Vec<EmailAttribute>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct EmailMessage {
73    pub summary: EmailMessageSummary,
74    pub body_text: Option<String>,
75    pub body_html: Option<String>,
76    pub attachments: Vec<EmailAttachmentMetadata>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80pub struct EmailAttachmentMetadata {
81    pub index: u32,
82    pub filename: Option<String>,
83    pub content_type: Option<String>,
84    pub size: Option<u64>,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub struct EmailAttachmentContent {
89    pub message_id: EmailMessageId,
90    pub attachment_index: u32,
91    pub filename: String,
92    pub content_type: String,
93    pub size: Option<u64>,
94    pub bytes: Vec<u8>,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub struct EmailMarkReadResult {
99    pub updated: u32,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct SendEmailResult {
104    pub accepted: bool,
105    pub message_id: Option<EmailMessageId>,
106    pub warnings: Vec<String>,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct EmailNotification {
111    pub id: crate::ids::MessageId,
112    pub mailbox_address: Option<EmailAddress>,
113    pub from_addr: Option<String>,
114    pub subject: String,
115    pub preview: Option<String>,
116    pub has_attachments: bool,
117    pub received_at: Option<String>,
118    pub attributes: Vec<EmailAttribute>,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct EmailAttribute {
123    pub key: String,
124    pub value: String,
125}
126
127impl EmailMessageId {
128    pub fn parse(input: impl AsRef<str>) -> crate::ImResult<Self> {
129        parse_non_empty("message_id", input).map(Self)
130    }
131
132    pub fn as_str(&self) -> &str {
133        &self.0
134    }
135}
136
137impl EmailFolder {
138    pub fn inbox() -> Self {
139        Self("inbox".to_string())
140    }
141
142    pub fn parse(input: impl AsRef<str>) -> crate::ImResult<Self> {
143        parse_non_empty("folder", input).map(Self)
144    }
145
146    pub fn as_str(&self) -> &str {
147        &self.0
148    }
149}
150
151impl EmailAddress {
152    pub fn parse(input: impl AsRef<str>) -> crate::ImResult<Self> {
153        let value = parse_non_empty("email_address", input)?;
154        if !value.contains('@') {
155            return Err(crate::ImError::invalid_input(
156                Some("email_address".to_string()),
157                "email address must contain @",
158            ));
159        }
160        Ok(Self(value))
161    }
162
163    pub fn as_str(&self) -> &str {
164        &self.0
165    }
166}
167
168impl Default for EmailInboxQuery {
169    fn default() -> Self {
170        Self {
171            folder: EmailFolder::inbox(),
172            limit: crate::ids::PageLimit(20),
173            offset: 0,
174            unread_only: false,
175        }
176    }
177}
178
179impl Default for EmailNotificationQuery {
180    fn default() -> Self {
181        Self {
182            limit: crate::ids::PageLimit(20),
183        }
184    }
185}
186
187fn parse_non_empty(field: &'static str, input: impl AsRef<str>) -> crate::ImResult<String> {
188    let value = input.as_ref().trim();
189    if value.is_empty() {
190        return Err(crate::ImError::invalid_input(
191            Some(field.to_string()),
192            format!("{field} must not be empty"),
193        ));
194    }
195    Ok(value.to_string())
196}