diff --git a/server/src/email_extract.rs b/server/src/email_extract.rs index 6afd0b6..1fce386 100644 --- a/server/src/email_extract.rs +++ b/server/src/email_extract.rs @@ -189,10 +189,6 @@ pub fn extract_calendar_metadata_from_mail( } } // Fallback: if body_html is still None, generate a minimal calendar HTML using all available metadata - let summary_val = summary.clone().unwrap_or_default(); - let organizer_val = organizer.clone().unwrap_or_default(); - let start_val = start_date.clone().unwrap_or_default(); - let end_val = end_date.clone().unwrap_or_default(); // Improved recurrence detection: check for common recurrence phrases in subject, HTML, and plain text body let mut has_recurrence = false; let recurrence_phrases = [ @@ -220,31 +216,54 @@ pub fn extract_calendar_metadata_from_mail( } } } - let recurrence_html: &str = if has_recurrence { "
Repeats
" } else { "" }; - let minimal_html = format!( - r#"
{}
{}
{} to {}
{}
"#, - html_escape::encode_text(&summary_val), - html_escape::encode_text(&organizer_val), - html_escape::encode_text(&start_val), - html_escape::encode_text(&end_val), - recurrence_html - ); let needs_ical_flex = summary.is_some() || start_date.is_some() || end_date.is_some() || has_recurrence; if needs_ical_flex { + let summary_val = summary.clone().unwrap_or_default(); + let organizer_val = organizer.clone().unwrap_or_default(); + let start_val = start_date.clone().unwrap_or_default(); + let end_val = end_date.clone().unwrap_or_default(); + let recurrence_display = if has_recurrence { "Repeats".to_string() } else { String::new() }; + let template = IcalSummaryTemplate { + summary: &summary_val, + local_fmt_start: &start_val, + local_fmt_end: &end_val, + organizer: &organizer_val, + organizer_cn: "", + all_days: vec![], + event_days: vec![], + caption: String::new(), + description_paragraphs: &[], + today: Some(chrono::Local::now().date_naive()), + recurrence_display, + }; + let fallback_html = template.render().unwrap_or_else(|_| String::from("
")); match &mut body_html { Some(existing) => { - if !existing.starts_with(&minimal_html) { - *existing = format!("{}{}", minimal_html, existing); + if !existing.starts_with(&fallback_html) { + *existing = format!("{}{}", fallback_html, existing); } }, None => { - body_html = Some(minimal_html); + body_html = Some(fallback_html); } } } - // Final guarantee: if body_html is still None, set to minimal ical-flex HTML with empty fields + // Final guarantee: if body_html is still None, set to minimal ical-flex HTML with empty fields using the template if body_html.is_none() { - body_html = Some("
to
".to_string()); + let template = IcalSummaryTemplate { + summary: "", + local_fmt_start: "", + local_fmt_end: "", + organizer: "", + organizer_cn: "", + all_days: vec![], + event_days: vec![], + caption: String::new(), + description_paragraphs: &[], + today: Some(chrono::Local::now().date_naive()), + recurrence_display: String::new(), + }; + body_html = Some(template.render().unwrap_or_else(|_| String::from("
"))); } // Improved fallback: extract summary, start_date, end_date, and recurrence from subject/body if not found @@ -314,11 +333,7 @@ pub fn extract_calendar_metadata_from_mail( } } // Try to detect recurrence from subject - if recurrence_html.is_empty() { - if subject.to_lowercase().contains("recurr") || subject.to_lowercase().contains("repeat") { - // recurrence_html assignment removed; handled at HTML generation - } - } + // recurrence detection and rendering is now handled by the template logic } // Try to extract summary from body if still missing if summary.is_none() { @@ -2154,19 +2169,20 @@ mod tests { // Calendar widget should be rendered let html = meta.body_html.expect("body_html"); println!("Rendered HTML for verification:\n{}", html); - // Check that the HTML contains the summary, organizer, start, and end times - assert!(html.contains(">McClure BLT<"), "HTML should contain the summary/title"); - assert!(html.contains(">calendar-notification@google.com<"), "HTML should contain the organizer"); - assert!(html.contains(">20250911 to 20260131<"), "HTML should contain the start and end times"); + // Check that the HTML contains the summary, organizer, start, and end times with labels + assert!(html.contains("Summary: McClure BLT"), "HTML should contain the labeled summary/title"); + assert!(html.contains("Organizer: calendar-notification@google.com"), "HTML should contain the labeled organizer"); + assert!(html.contains("Start: 20250911"), "HTML should contain the labeled start time"); + assert!(html.contains("End: 20260131"), "HTML should contain the labeled end time"); if !html.contains("ical-flex") { println!("FAIL: html did not contain 'ical-flex':\n{}", html); } assert!(html.contains("ical-flex"), "Calendar widget should be rendered"); // Recurrence info should be present - if !(html.contains("Repeats") || html.contains("recurr") || html.contains("RRULE")) { + if !(html.contains("Repeats: Repeats") || html.contains("recurr") || html.contains("RRULE")) { println!("FAIL: html did not contain recurrence info:\n{}", html); } - assert!(html.contains("Repeats") || html.contains("recurr") || html.contains("RRULE"), "Recurrence info should be present in HTML"); + assert!(html.contains("Repeats: Repeats") || html.contains("recurr") || html.contains("RRULE"), "Recurrence info should be present in HTML"); } use super::*; #[test]