gmail_mcp_server/
reademail.rs

1use crate::{EmailResponse, EmailSummary};
2use html2text::from_read as html_to_text;
3use regex::Regex;
4use tokio::task;
5use tracing::{error, info, warn};
6
7/// Reads emails from Gmail and processes them
8pub async fn read_emails(max_results: u32) -> Result<String, Box<dyn std::error::Error>> {
9    info!("Starting to read {} emails from Gmail", max_results);
10
11    let json = match crate::run(max_results).await {
12        Ok(json) => {
13            info!("Gmail API response received ({} bytes)", json.len());
14            json
15        }
16        Err(e) => {
17            error!("Failed to fetch emails from Gmail API: {}", e);
18            return Err(e);
19        }
20    };
21
22    let mut response: EmailResponse = serde_json::from_str(&json)?;
23
24    if response.emails.is_empty() {
25        warn!("No emails found in Gmail response");
26        return Ok(serde_json::to_string_pretty(&response)?);
27    }
28
29    info!("Processing {} emails", response.emails.len());
30    for email in response.emails.iter_mut() {
31        convert_html_to_text(email).await;
32    }
33
34    info!("Email processing completed");
35    Ok(serde_json::to_string_pretty(&response)?)
36}
37
38/// Convert HTML to text and remove URLs
39pub async fn convert_html_to_text(summary: &mut EmailSummary) {
40    // Convert HTML to text if needed
41    if summary.body_raw.starts_with('<') {
42        let html_body = summary.body_raw.clone();
43        let plain_text = task::spawn_blocking(move || html_to_text(html_body.as_bytes(), 100))
44            .await
45            .unwrap();
46        summary.body_raw = plain_text;
47    }
48
49    // Remove URLs from text (simplified inline implementation)
50    summary.body_raw = remove_urls_simple(&summary.body_raw);
51}
52
53/// Simple URL removal function
54fn remove_urls_simple(text: &str) -> String {
55    // Simple regex to match common URL patterns
56    let url_regex = Regex::new(r"https?://[^\s]+|www\.[^\s]+").unwrap_or_else(|_| {
57        // If regex fails, return a regex that matches nothing
58        Regex::new(r"$^").unwrap()
59    });
60
61    url_regex.replace_all(text, "").to_string()
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_url_removal() {
70        let test_text = "Check out this link: https://example.com and this one too: www.test.org. More text here.";
71        let cleaned = remove_urls_simple(test_text);
72
73        assert!(!cleaned.contains("https://example.com"));
74        assert!(!cleaned.contains("www.test.org"));
75        assert!(cleaned.contains("Check out this link:"));
76        assert!(cleaned.contains("More text here."));
77    }
78
79    #[tokio::test]
80    async fn test_email_processing_with_urls() {
81        let mut email = EmailSummary {
82            id: "test_id".to_string(),
83            from: "test@example.com".to_string(),
84            subject: "Test Subject".to_string(),
85            snippet: "Test snippet".to_string(),
86            body_raw: "Check this out: https://example.com\n\nVisit www.test.org for more info.\n\nThanks!".to_string(),
87        };
88
89        convert_html_to_text(&mut email).await;
90
91        assert!(!email.body_raw.contains("https://example.com"));
92        assert!(!email.body_raw.contains("www.test.org"));
93        assert!(email.body_raw.contains("Check this out:"));
94        assert!(email.body_raw.contains("Thanks!"));
95    }
96}