Skip to main content

max / goingson

5.7 KB · 157 lines History Blame Raw
1 //! JMAP Mailbox operations.
2
3 use super::client::JmapClient;
4 use super::types::JmapMailbox;
5 use serde_json::json;
6
7 impl JmapClient {
8 /// Lists all mailboxes for the account.
9 pub async fn list_mailboxes(&mut self) -> Result<Vec<JmapMailbox>, String> {
10 let account_id = self.account_id().await?;
11
12 let args = json!({
13 "accountId": account_id,
14 "ids": null,
15 "properties": [
16 "id", "name", "parentId", "role", "sortOrder",
17 "totalEmails", "unreadEmails", "totalThreads", "unreadThreads",
18 "myRights"
19 ]
20 });
21
22 let response = self.call("Mailbox/get", args).await?;
23
24 let mailboxes: Vec<JmapMailbox> = serde_json::from_value(response.data["list"].clone())
25 .map_err(|e| format!("Failed to parse mailboxes: {}", e))?;
26
27 Ok(mailboxes)
28 }
29
30 /// Finds a mailbox by role (e.g., "inbox", "archive", "sent", "trash", "drafts").
31 pub async fn find_mailbox_by_role(&mut self, role: &str) -> Result<Option<JmapMailbox>, String> {
32 let mailboxes = self.list_mailboxes().await?;
33 Ok(mailboxes
34 .into_iter()
35 .find(|m| m.role.as_deref() == Some(role)))
36 }
37
38 /// Finds a mailbox by name.
39 pub async fn find_mailbox_by_name(&mut self, name: &str) -> Result<Option<JmapMailbox>, String> {
40 let mailboxes = self.list_mailboxes().await?;
41 Ok(mailboxes.into_iter().find(|m| m.name == name))
42 }
43
44 /// Gets the inbox mailbox.
45 pub async fn inbox(&mut self) -> Result<JmapMailbox, String> {
46 self.find_mailbox_by_role("inbox")
47 .await?
48 .ok_or_else(|| "Inbox mailbox not found".to_string())
49 }
50
51 /// Gets the archive mailbox (or creates one if it doesn't exist).
52 pub async fn archive_mailbox(&mut self) -> Result<JmapMailbox, String> {
53 // Try to find by role first
54 if let Some(mailbox) = self.find_mailbox_by_role("archive").await? {
55 return Ok(mailbox);
56 }
57
58 // Try to find by name
59 if let Some(mailbox) = self.find_mailbox_by_name("Archive").await? {
60 return Ok(mailbox);
61 }
62
63 // Mailbox doesn't exist - we could create it here, but for now just error
64 Err("Archive mailbox not found. Please create an 'Archive' folder in your email client.".to_string())
65 }
66
67 /// Gets the sent mailbox.
68 pub async fn sent_mailbox(&mut self) -> Result<JmapMailbox, String> {
69 self.find_mailbox_by_role("sent")
70 .await?
71 .ok_or_else(|| "Sent mailbox not found".to_string())
72 }
73
74 /// Gets the drafts mailbox.
75 pub async fn drafts_mailbox(&mut self) -> Result<JmapMailbox, String> {
76 self.find_mailbox_by_role("drafts")
77 .await?
78 .ok_or_else(|| "Drafts mailbox not found".to_string())
79 }
80
81 /// Gets the trash mailbox.
82 pub async fn trash_mailbox(&mut self) -> Result<JmapMailbox, String> {
83 self.find_mailbox_by_role("trash")
84 .await?
85 .ok_or_else(|| "Trash mailbox not found".to_string())
86 }
87
88 /// Moves an email to a different mailbox.
89 pub async fn move_email(&mut self, email_id: &str, to_mailbox_id: &str) -> Result<(), String> {
90 let account_id = self.account_id().await?;
91
92 // First, get the current mailboxes for this email
93 let get_args = json!({
94 "accountId": account_id,
95 "ids": [email_id],
96 "properties": ["mailboxIds"]
97 });
98
99 let get_response = self.call("Email/get", get_args).await?;
100 let emails: Vec<serde_json::Value> =
101 serde_json::from_value(get_response.data["list"].clone())
102 .map_err(|e| format!("Failed to parse email: {}", e))?;
103
104 if emails.is_empty() {
105 return Err("Email not found".to_string());
106 }
107
108 // Build update to remove from all current mailboxes and add to target
109 let current_mailboxes = emails[0]["mailboxIds"]
110 .as_object()
111 .cloned()
112 .unwrap_or_default();
113
114 let mut mailbox_updates = serde_json::Map::new();
115 // Remove from all current mailboxes
116 for mailbox_id in current_mailboxes.keys() {
117 mailbox_updates.insert(mailbox_id.clone(), json!(null));
118 }
119 // Add to target mailbox
120 mailbox_updates.insert(to_mailbox_id.to_string(), json!(true));
121
122 let mut email_patch = serde_json::Map::new();
123 email_patch.insert("mailboxIds".to_string(), json!(mailbox_updates));
124 let mut update_map = serde_json::Map::new();
125 update_map.insert(email_id.to_string(), json!(email_patch));
126 let update_args = json!({
127 "accountId": account_id,
128 "update": update_map
129 });
130
131 let update_response = self.call("Email/set", update_args).await?;
132
133 // Check for update errors
134 if let Some(not_updated) = update_response.data["notUpdated"].as_object() {
135 if let Some(error) = not_updated.get(email_id) {
136 let error_type = error["type"].as_str().unwrap_or("unknown");
137 let description = error["description"].as_str().unwrap_or("Unknown error");
138 return Err(format!("Failed to move email ({}): {}", error_type, description));
139 }
140 }
141
142 Ok(())
143 }
144
145 /// Archives an email (moves to archive mailbox).
146 pub async fn archive_email(&mut self, email_id: &str) -> Result<(), String> {
147 let archive = self.archive_mailbox().await?;
148 self.move_email(email_id, &archive.id).await
149 }
150
151 /// Moves an email back to inbox from archive.
152 pub async fn unarchive_email(&mut self, email_id: &str) -> Result<(), String> {
153 let inbox = self.inbox().await?;
154 self.move_email(email_id, &inbox.id).await
155 }
156 }
157