//! JMAP Mailbox operations. use super::client::JmapClient; use super::types::JmapMailbox; use serde_json::json; impl JmapClient { /// Lists all mailboxes for the account. pub async fn list_mailboxes(&mut self) -> Result, String> { let account_id = self.account_id().await?; let args = json!({ "accountId": account_id, "ids": null, "properties": [ "id", "name", "parentId", "role", "sortOrder", "totalEmails", "unreadEmails", "totalThreads", "unreadThreads", "myRights" ] }); let response = self.call("Mailbox/get", args).await?; let mailboxes: Vec = serde_json::from_value(response.data["list"].clone()) .map_err(|e| format!("Failed to parse mailboxes: {}", e))?; Ok(mailboxes) } /// Finds a mailbox by role (e.g., "inbox", "archive", "sent", "trash", "drafts"). pub async fn find_mailbox_by_role(&mut self, role: &str) -> Result, String> { let mailboxes = self.list_mailboxes().await?; Ok(mailboxes .into_iter() .find(|m| m.role.as_deref() == Some(role))) } /// Finds a mailbox by name. pub async fn find_mailbox_by_name(&mut self, name: &str) -> Result, String> { let mailboxes = self.list_mailboxes().await?; Ok(mailboxes.into_iter().find(|m| m.name == name)) } /// Gets the inbox mailbox. pub async fn inbox(&mut self) -> Result { self.find_mailbox_by_role("inbox") .await? .ok_or_else(|| "Inbox mailbox not found".to_string()) } /// Gets the archive mailbox (or creates one if it doesn't exist). pub async fn archive_mailbox(&mut self) -> Result { // Try to find by role first if let Some(mailbox) = self.find_mailbox_by_role("archive").await? { return Ok(mailbox); } // Try to find by name if let Some(mailbox) = self.find_mailbox_by_name("Archive").await? { return Ok(mailbox); } // Mailbox doesn't exist - we could create it here, but for now just error Err("Archive mailbox not found. Please create an 'Archive' folder in your email client.".to_string()) } /// Gets the sent mailbox. pub async fn sent_mailbox(&mut self) -> Result { self.find_mailbox_by_role("sent") .await? .ok_or_else(|| "Sent mailbox not found".to_string()) } /// Gets the drafts mailbox. pub async fn drafts_mailbox(&mut self) -> Result { self.find_mailbox_by_role("drafts") .await? .ok_or_else(|| "Drafts mailbox not found".to_string()) } /// Gets the trash mailbox. pub async fn trash_mailbox(&mut self) -> Result { self.find_mailbox_by_role("trash") .await? .ok_or_else(|| "Trash mailbox not found".to_string()) } /// Moves an email to a different mailbox. pub async fn move_email(&mut self, email_id: &str, to_mailbox_id: &str) -> Result<(), String> { let account_id = self.account_id().await?; // First, get the current mailboxes for this email let get_args = json!({ "accountId": account_id, "ids": [email_id], "properties": ["mailboxIds"] }); let get_response = self.call("Email/get", get_args).await?; let emails: Vec = serde_json::from_value(get_response.data["list"].clone()) .map_err(|e| format!("Failed to parse email: {}", e))?; if emails.is_empty() { return Err("Email not found".to_string()); } // Build update to remove from all current mailboxes and add to target let current_mailboxes = emails[0]["mailboxIds"] .as_object() .cloned() .unwrap_or_default(); let mut mailbox_updates = serde_json::Map::new(); // Remove from all current mailboxes for mailbox_id in current_mailboxes.keys() { mailbox_updates.insert(mailbox_id.clone(), json!(null)); } // Add to target mailbox mailbox_updates.insert(to_mailbox_id.to_string(), json!(true)); let mut email_patch = serde_json::Map::new(); email_patch.insert("mailboxIds".to_string(), json!(mailbox_updates)); let mut update_map = serde_json::Map::new(); update_map.insert(email_id.to_string(), json!(email_patch)); let update_args = json!({ "accountId": account_id, "update": update_map }); let update_response = self.call("Email/set", update_args).await?; // Check for update errors if let Some(not_updated) = update_response.data["notUpdated"].as_object() { if let Some(error) = not_updated.get(email_id) { let error_type = error["type"].as_str().unwrap_or("unknown"); let description = error["description"].as_str().unwrap_or("Unknown error"); return Err(format!("Failed to move email ({}): {}", error_type, description)); } } Ok(()) } /// Archives an email (moves to archive mailbox). pub async fn archive_email(&mut self, email_id: &str) -> Result<(), String> { let archive = self.archive_mailbox().await?; self.move_email(email_id, &archive.id).await } /// Moves an email back to inbox from archive. pub async fn unarchive_email(&mut self, email_id: &str) -> Result<(), String> { let inbox = self.inbox().await?; self.move_email(email_id, &inbox.id).await } }