use regex::Regex; use crate::config::Config; use crate::connect::ImapSession; use crate::{connect, Email}; const MAX_FETCH: u32 = 50; /// Refresh inbox using NOOP + fetch. Reconnects on error. pub(crate) fn refresh( session: &mut Option, config: &Config, ) -> Result, String> { // If we have a session, try NOOP to keep alive / detect changes if let Some(s) = session.as_mut() { if s.noop().is_ok() { return fetch_inbox(s); } } // Session is dead or missing — reconnect *session = None; let mut new_session = connect::connect(config)?; let result = fetch_inbox(&mut new_session); *session = Some(new_session); result } fn fetch_range(exists: u32) -> String { let start = exists.saturating_sub(MAX_FETCH - 1).max(1); format!("{}:{}", start, exists) } fn fetch_inbox(session: &mut ImapSession) -> Result, String> { match session { ImapSession::Plain(s) => { let mailbox = s.select("INBOX").map_err(|e| e.to_string())?; if mailbox.exists == 0 { return Ok(Vec::new()); } let range = fetch_range(mailbox.exists); let messages = s .fetch(range, "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]") .map_err(|e| e.to_string())?; let mut emails = parse_emails(&messages); emails.reverse(); Ok(emails) } ImapSession::Tls(s) => { let mailbox = s.select("INBOX").map_err(|e| e.to_string())?; if mailbox.exists == 0 { return Ok(Vec::new()); } let range = fetch_range(mailbox.exists); let messages = s .fetch(range, "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]") .map_err(|e| e.to_string())?; let mut emails = parse_emails(&messages); emails.reverse(); Ok(emails) } } } fn parse_emails(fetches: &[imap::types::Fetch]) -> Vec { let mut emails = Vec::new(); for message in fetches { if let Some(body) = message.header() { let header = String::from_utf8_lossy(body); let mut subject = String::new(); let mut from = String::new(); let mut date = String::new(); for line in header.lines() { if let Some(val) = line.strip_prefix("Subject: ") { subject = val.to_string(); } else if let Some(val) = line.strip_prefix("From: ") { from = val.to_string(); } else if let Some(val) = line.strip_prefix("Date: ") { date = Regex::new(r#" \(...\)$"#).unwrap().replace_all(val, "").to_string(); } } emails.push(Email { subject, from, date }); } } emails }