use once_cell::sync::Lazy;
use regex::Regex;
static QUOTE_HEADERS: Lazy<Vec<Regex>> = Lazy::new(|| {
[
r"(?m)^On .{10,80} wrote:\s*$",
r"(?m)^-{2,}\s*Original Message\s*-{2,}\s*$",
r"(?m)^From:\s+.+\nSent:\s+",
r"(?m)^On .{10,80}, at .{4,20}, .{1,60} wrote:\s*$",
r"(?m)^Am .{10,80} schrieb .{1,60}:\s*$",
r"(?m)^Le .{10,80} a écrit\s*:\s*$",
r"(?m)^El .{10,80} escribi[óo]\s*:\s*$",
r"(?m)^.{10,120} wrote:\s*$",
r"(?m)^\s*\n(>{1}\s?.+\n){2,}",
]
.iter()
.filter_map(|pattern| Regex::new(pattern).ok())
.collect()
});
pub fn strip_quotes(body: &str) -> String {
let mut earliest_quote_start = body.len();
for pattern in QUOTE_HEADERS.iter() {
if let Some(m) = pattern.find(body) {
if m.start() < earliest_quote_start {
earliest_quote_start = m.start();
}
}
}
if earliest_quote_start == 0 {
let lines: Vec<&str> = body.lines().collect();
let first_non_quoted = lines
.iter()
.position(|line| !line.starts_with('>') && !line.trim().is_empty());
if let Some(idx) = first_non_quoted {
let end = lines[idx..]
.iter()
.position(|line| line.starts_with('>'))
.map(|e| e + idx)
.unwrap_or(lines.len());
return lines[idx..end].join("\n");
}
}
body[..earliest_quote_start].to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gmail_quote() {
let body = "Thanks for the update!\n\nOn Thu, Feb 5, 2026 at 10:00 AM Alice <alice@example.com> wrote:\n> Original message here\n> More text\n";
let result = strip_quotes(body);
assert!(result.contains("Thanks for the update!"));
assert!(!result.contains("Original message here"));
}
#[test]
fn test_outlook_quote() {
let body = "Sounds good to me.\n\n-----Original Message-----\nFrom: Alice\nSent: Thursday\nSubject: Hello\n\nOriginal text\n";
let result = strip_quotes(body);
assert!(result.contains("Sounds good to me."));
assert!(!result.contains("Original text"));
}
#[test]
fn test_no_quotes() {
let body = "Just a plain email with no quoted content.\n\nSecond paragraph.\n";
let result = strip_quotes(body);
assert_eq!(result, body);
}
#[test]
fn test_german_quote_header() {
let body = "Danke für die Info!\n\nAm 05.02.2026 um 10:00 schrieb Alice Müller:\n> Originalnachricht\n";
let result = strip_quotes(body);
assert!(result.contains("Danke"));
assert!(!result.contains("Originalnachricht"));
}
#[test]
fn test_forwarded_message_preserved() {
let body = "FYI, see below.\n\n-------- Forwarded Message --------\nFrom: Alice\nSubject: Hello\n\nForwarded content\n";
let result = strip_quotes(body);
assert!(result.contains("FYI"), "intro should be present");
assert!(
result.contains("Forwarded content"),
"forwarded body should not be stripped"
);
}
#[test]
fn test_generic_wrote_pattern() {
let body = "I agree.\n\nJohn Doe <john@example.com> wrote:\n> Some quoted text\n";
let result = strip_quotes(body);
assert!(result.contains("I agree."));
assert!(!result.contains("Some quoted text"));
}
}