From a8a5089ed3bc65534dea7236855854ef5791c643 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Tue, 19 Aug 2025 11:17:11 -0700 Subject: [PATCH] server: render calendar summary before any pre-existing text --- Cargo.lock | 145 ++++++++++++++++++++++++++++++------ server/Cargo.toml | 1 + server/src/email_extract.rs | 24 +++++- 3 files changed, 146 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f589ac..acac77a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1364,7 +1364,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa 1.0.15", - "phf", + "phf 0.11.3", "smallvec 1.15.1", ] @@ -1377,7 +1377,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa 1.0.15", - "phf", + "phf 0.11.3", "smallvec 1.15.1", ] @@ -2053,7 +2053,7 @@ version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" dependencies = [ - "unicode-width", + "unicode-width 0.2.1", ] [[package]] @@ -2518,6 +2518,33 @@ dependencies = [ "utf8-width", ] +[[package]] +name = "html2text" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74cda84f06c1cc83476f79ae8e2e892b626bdadafcb227baec54c918cadc18a0" +dependencies = [ + "html5ever 0.26.0", + "markup5ever 0.11.0", + "tendril", + "unicode-width 0.1.14", + "xml5ever", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever 0.11.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "html5ever" version = "0.29.1" @@ -3250,6 +3277,7 @@ dependencies = [ "futures 0.3.31", "headers", "html-escape", + "html2text", "ical", "icalendar", "letterbox-notmuch 0.17.34", @@ -3554,6 +3582,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "markup5ever" version = "0.14.1" @@ -3561,8 +3603,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf", - "phf_codegen", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", @@ -4353,6 +4395,15 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + [[package]] name = "phf" version = "0.11.3" @@ -4360,7 +4411,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", ] [[package]] @@ -4369,8 +4430,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", ] [[package]] @@ -4379,7 +4450,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] @@ -4389,20 +4460,29 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", "syn 2.0.104", ] +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] @@ -5434,8 +5514,8 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf", - "phf_codegen", + "phf 0.11.3", + "phf_codegen 0.11.3", "precomputed-hash", "servo_arc", "smallvec 1.15.1", @@ -5453,8 +5533,8 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf", - "phf_codegen", + "phf 0.11.3", + "phf_codegen 0.11.3", "precomputed-hash", "servo_arc", "smallvec 1.15.1", @@ -5687,6 +5767,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "siphasher" version = "1.0.1" @@ -5995,7 +6081,7 @@ checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot 0.12.4", - "phf_shared", + "phf_shared 0.11.3", "precomputed-hash", "serde", ] @@ -6006,8 +6092,8 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", ] @@ -7077,6 +7163,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-width" version = "0.2.1" @@ -7381,8 +7473,8 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" dependencies = [ - "phf", - "phf_codegen", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", ] @@ -7782,6 +7874,17 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +[[package]] +name = "xml5ever" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" +dependencies = [ + "log", + "mac", + "markup5ever 0.11.0", +] + [[package]] name = "xtracing" version = "0.3.2" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1ed0028..3156f0e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -12,6 +12,7 @@ version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +html2text = "0.6" ammonia = "4.1.0" anyhow = "1.0.98" askama = { version = "0.14.0", features = ["derive"] } diff --git a/server/src/email_extract.rs b/server/src/email_extract.rs index b77a0dc..d4273df 100644 --- a/server/src/email_extract.rs +++ b/server/src/email_extract.rs @@ -328,25 +328,43 @@ pub fn extract_alternative( return extract_related(sp, part_addr); } } + let mut ical_summary: Option = None; for sp in &m.subparts { if sp.ctype.mimetype.as_str() == TEXT_CALENDAR { let body = sp.get_body()?; let summary = render_ical_summary(&body)?; - return Ok(Body::html(summary)); + ical_summary = Some(summary); + break; } } for sp in &m.subparts { if sp.ctype.mimetype.as_str() == TEXT_HTML { let body = sp.get_body()?; - return Ok(Body::html(body)); + if let Some(ref summary) = ical_summary { + // Prepend summary to HTML body + let combined = format!("{}
{}", summary, body); + return Ok(Body::html(combined)); + } else { + return Ok(Body::html(body)); + } } } for sp in &m.subparts { if sp.ctype.mimetype.as_str() == TEXT_PLAIN { let body = sp.get_body()?; - return Ok(Body::text(body)); + if let Some(ref summary) = ical_summary { + // Prepend summary to plain text body (strip HTML tags) + let summary_text = html2text::from_read(summary.as_bytes(), 80); + let combined = format!("{}\n\n{}", summary_text.trim(), body); + return Ok(Body::text(combined)); + } else { + return Ok(Body::text(body)); + } } } + if let Some(summary) = ical_summary { + return Ok(Body::html(summary)); + } Err(ServerError::StringError(format!( "extract_alternative failed to find suitable subpart, searched: {:?}", handled_types