gmail_mcp_server/
reademail.rs1use crate::{EmailResponse, EmailSummary};
2use html2text::from_read as html_to_text;
3use regex::Regex;
4use tokio::task;
5use tracing::{error, info, warn};
6
7pub 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
38pub async fn convert_html_to_text(summary: &mut EmailSummary) {
40 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 summary.body_raw = remove_urls_simple(&summary.body_raw);
51}
52
53fn remove_urls_simple(text: &str) -> String {
55 let url_regex = Regex::new(r"https?://[^\s]+|www\.[^\s]+").unwrap_or_else(|_| {
57 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}