Skip to main content

max / pter

Apply rustfmt across the crate Formatting only, no behavior change. cargo check --all-targets passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-21 23:46 UTC
Commit: 02dc9bf9f698869ea38e30bb80d5624c4ff98d93
Parent: c5fd9e1
6 files changed, +93 insertions, -44 deletions
@@ -94,5 +94,11 @@ fn bench_large(c: &mut Criterion) {
94 94 });
95 95 }
96 96
97 - criterion_group!(benches, bench_simple, bench_newsletter, bench_reply_chain, bench_large);
97 + criterion_group!(
98 + benches,
99 + bench_simple,
100 + bench_newsletter,
101 + bench_reply_chain,
102 + bench_large
103 + );
98 104 criterion_main!(benches);
M src/convert.rs +18 -13
@@ -537,10 +537,7 @@ mod tests {
537 537
538 538 #[test]
539 539 fn unordered_list() {
540 - assert_eq!(
541 - convert("<ul><li>one</li><li>two</li></ul>"),
542 - "- one\n- two"
543 - );
540 + assert_eq!(convert("<ul><li>one</li><li>two</li></ul>"), "- one\n- two");
544 541 }
545 542
546 543 #[test]
@@ -588,9 +585,7 @@ mod tests {
588 585 // After a nested <ul> closes, `list_depth -= 1` must execute to return
589 586 // to outer scope. If mutated to `+= 1` or `/= 1`, list_depth stays
590 587 // elevated and the SECOND top-level list ends up incorrectly indented.
591 - let md = convert(
592 - "<ul><li>A<ul><li>B</li></ul></li></ul><ul><li>C</li></ul>",
593 - );
588 + let md = convert("<ul><li>A<ul><li>B</li></ul></li></ul><ul><li>C</li></ul>");
594 589 // The second list's "C" item must appear at column 0, not indented.
595 590 // We check the exact substring "\n- C" (newline then no leading whitespace).
596 591 assert!(
@@ -608,16 +603,23 @@ mod tests {
608 603 fn ordered_list_decrements_depth_after_nesting() {
609 604 // Same shape but with <ol> — exercises the L218 `-= 1` mutation in the
610 605 // OrderedList block, distinct from UnorderedList's L208.
611 - let md = convert(
612 - "<ol><li>A<ol><li>B</li></ol></li></ol><ol><li>C</li></ol>",
606 + let md = convert("<ol><li>A<ol><li>B</li></ol></li></ol><ol><li>C</li></ol>");
607 + assert!(
608 + md.contains("\n1. C"),
609 + "second ol must restart at depth 1: {md:?}"
610 + );
611 + assert!(
612 + !md.contains("\n 1. C"),
613 + "second ol indented incorrectly: {md:?}"
613 614 );
614 - assert!(md.contains("\n1. C"), "second ol must restart at depth 1: {md:?}");
615 - assert!(!md.contains("\n 1. C"), "second ol indented incorrectly: {md:?}");
616 615 }
617 616
618 617 #[test]
619 618 fn blockquote() {
620 - assert_eq!(convert("<blockquote>quoted text</blockquote>"), "> quoted text");
619 + assert_eq!(
620 + convert("<blockquote>quoted text</blockquote>"),
621 + "> quoted text"
622 + );
621 623 }
622 624
623 625 #[test]
@@ -639,7 +641,10 @@ mod tests {
639 641
640 642 #[test]
641 643 fn horizontal_rule() {
642 - assert_eq!(convert("<p>above</p><hr><p>below</p>"), "above\n\n---\n\nbelow");
644 + assert_eq!(
645 + convert("<p>above</p><hr><p>below</p>"),
646 + "above\n\n---\n\nbelow"
647 + );
643 648 }
644 649
645 650 #[test]
M src/elements.rs +26 -13
@@ -41,9 +41,7 @@ pub enum InlineKind {
41 41 pub fn classify(el: &Element) -> ElementAction {
42 42 match el.name() {
43 43 // Skip entirely
44 - "script" | "style" | "head" | "meta" | "link" | "title" | "noscript" => {
45 - ElementAction::Skip
46 - }
44 + "script" | "style" | "head" | "meta" | "link" | "title" | "noscript" => ElementAction::Skip,
47 45
48 46 // Block elements
49 47 "p" => ElementAction::Block(BlockKind::Paragraph),
@@ -65,9 +63,7 @@ pub fn classify(el: &Element) -> ElementAction {
65 63 ElementAction::Transparent
66 64 }
67 65 "div" | "section" | "article" | "main" | "header" | "footer" | "nav" | "aside"
68 - | "figure" | "figcaption" | "details" | "summary" => {
69 - ElementAction::Block(BlockKind::Div)
70 - }
66 + | "figure" | "figcaption" | "details" | "summary" => ElementAction::Block(BlockKind::Div),
71 67
72 68 // Inline elements
73 69 "strong" | "b" => ElementAction::Inline(InlineKind::Bold),
@@ -189,22 +185,34 @@ mod tests {
189 185
190 186 #[test]
191 187 fn classify_h1_is_heading_1() {
192 - assert!(matches!(classify_tag("h1"), ElementAction::Block(BlockKind::Heading(1))));
188 + assert!(matches!(
189 + classify_tag("h1"),
190 + ElementAction::Block(BlockKind::Heading(1))
191 + ));
193 192 }
194 193
195 194 #[test]
196 195 fn classify_h4_is_heading_4() {
197 - assert!(matches!(classify_tag("h4"), ElementAction::Block(BlockKind::Heading(4))));
196 + assert!(matches!(
197 + classify_tag("h4"),
198 + ElementAction::Block(BlockKind::Heading(4))
199 + ));
198 200 }
199 201
200 202 #[test]
201 203 fn classify_h5_is_heading_5() {
202 - assert!(matches!(classify_tag("h5"), ElementAction::Block(BlockKind::Heading(5))));
204 + assert!(matches!(
205 + classify_tag("h5"),
206 + ElementAction::Block(BlockKind::Heading(5))
207 + ));
203 208 }
204 209
205 210 #[test]
206 211 fn classify_h6_is_heading_6() {
207 - assert!(matches!(classify_tag("h6"), ElementAction::Block(BlockKind::Heading(6))));
212 + assert!(matches!(
213 + classify_tag("h6"),
214 + ElementAction::Block(BlockKind::Heading(6))
215 + ));
208 216 }
209 217
210 218 #[test]
@@ -214,12 +222,18 @@ mod tests {
214 222
215 223 #[test]
216 224 fn classify_table_is_block_table() {
217 - assert!(matches!(classify_tag("table"), ElementAction::Block(BlockKind::Table)));
225 + assert!(matches!(
226 + classify_tag("table"),
227 + ElementAction::Block(BlockKind::Table)
228 + ));
218 229 }
219 230
220 231 #[test]
221 232 fn classify_strong_is_inline_bold() {
222 - assert!(matches!(classify_tag("strong"), ElementAction::Inline(InlineKind::Bold)));
233 + assert!(matches!(
234 + classify_tag("strong"),
235 + ElementAction::Inline(InlineKind::Bold)
236 + ));
223 237 }
224 238
225 239 // -- is_tracking_pixel: each || arm needs its own positive test --
@@ -396,4 +410,3 @@ mod tests {
396 410 assert!(!div_is_hidden(""));
397 411 }
398 412 }
399 -
M src/replies.rs +11 -5
@@ -1,5 +1,5 @@
1 - use scraper::node::Node;
2 1 use scraper::ElementRef;
2 + use scraper::node::Node;
3 3
4 4 /// Check if an element marks the beginning of a quoted reply.
5 5 ///
@@ -118,7 +118,7 @@ fn is_reply_class(class: &str) -> bool {
118 118 | "tutanota_quote"
119 119 | "moz-cite-prefix" // Thunderbird
120 120 | "zmail_extra" // Zoho
121 - | "WordSection1" // Outlook (sometimes wraps replies)
121 + | "WordSection1" // Outlook (sometimes wraps replies)
122 122 )
123 123 })
124 124 }
@@ -227,12 +227,16 @@ mod tests {
227 227
228 228 #[test]
229 229 fn attribution_on_wrote() {
230 - assert!(is_attribution_text("On Mon, Jan 5, 2026 at 3:00 PM Alice <alice@example.com> wrote:"));
230 + assert!(is_attribution_text(
231 + "On Mon, Jan 5, 2026 at 3:00 PM Alice <alice@example.com> wrote:"
232 + ));
231 233 }
232 234
233 235 #[test]
234 236 fn attribution_forwarded() {
235 - assert!(is_attribution_text("---------- Forwarded message ----------"));
237 + assert!(is_attribution_text(
238 + "---------- Forwarded message ----------"
239 + ));
236 240 }
237 241
238 242 #[test]
@@ -336,7 +340,9 @@ mod tests {
336 340
337 341 #[test]
338 342 fn attribution_french_le_with_colon_space() {
339 - assert!(is_attribution_text("Le lundi 5 janvier 2026, Alice a écrit :"));
343 + assert!(is_attribution_text(
344 + "Le lundi 5 janvier 2026, Alice a écrit :"
345 + ));
340 346 }
341 347
342 348 #[test]
M src/tables.rs +3 -6
@@ -227,8 +227,7 @@ mod tests {
227 227
228 228 #[test]
229 229 fn role_grid_is_data() {
230 - let doc =
231 - parse_table(r#"<table role="grid"><tr><td>Alice</td><td>30</td></tr></table>"#);
230 + let doc = parse_table(r#"<table role="grid"><tr><td>Alice</td><td>30</td></tr></table>"#);
232 231 assert!(is_data_table(select_table(&doc)));
233 232 }
234 233
@@ -312,8 +311,7 @@ mod tests {
312 311 fn role_table_is_data() {
313 312 // role="table" → data. Catches L22 == "grid" mutating to != (which would
314 313 // make grid not match) AND covers the parallel `|| role == "table"` arm.
315 - let doc =
316 - parse_table(r#"<table role="table"><tr><td>a</td></tr></table>"#);
314 + let doc = parse_table(r#"<table role="table"><tr><td>a</td></tr></table>"#);
317 315 assert!(is_data_table(select_table(&doc)));
318 316 }
319 317
@@ -321,8 +319,7 @@ mod tests {
321 319 fn role_unknown_falls_through_to_structural() {
322 320 // Unknown role → no early decision; structural rules apply.
323 321 // Single-cell single-row layout table → not data.
324 - let doc =
325 - parse_table(r#"<table role="banner"><tr><td>only one cell</td></tr></table>"#);
322 + let doc = parse_table(r#"<table role="banner"><tr><td>only one cell</td></tr></table>"#);
326 323 assert!(!is_data_table(select_table(&doc)));
327 324 }
328 325
@@ -3,9 +3,33 @@ use proptest::prelude::*;
3 3 // Strategy: generate arbitrary HTML-like strings
4 4 fn html_fragment() -> impl Strategy<Value = String> {
5 5 let tags = prop::sample::select(vec![
6 - "p", "div", "span", "strong", "em", "a", "h1", "h2", "h3",
7 - "ul", "ol", "li", "blockquote", "pre", "code", "br", "hr",
8 - "img", "table", "tr", "td", "th", "b", "i", "del", "sup", "sub",
6 + "p",
7 + "div",
8 + "span",
9 + "strong",
10 + "em",
11 + "a",
12 + "h1",
13 + "h2",
14 + "h3",
15 + "ul",
16 + "ol",
17 + "li",
18 + "blockquote",
19 + "pre",
20 + "code",
21 + "br",
22 + "hr",
23 + "img",
24 + "table",
25 + "tr",
26 + "td",
27 + "th",
28 + "b",
29 + "i",
30 + "del",
31 + "sup",
32 + "sub",
9 33 ]);
10 34
11 35 let text = "[a-zA-Z0-9 .,!?]{0,100}";
@@ -15,9 +39,7 @@ fn html_fragment() -> impl Strategy<Value = String> {
15 39 // Plain text
16 40 text.prop_map(|s| s),
17 41 // Opening + closing tag with text
18 - (tags.clone(), text).prop_map(|(tag, content)| {
19 - format!("<{tag}>{content}</{tag}>")
20 - }),
42 + (tags.clone(), text).prop_map(|(tag, content)| { format!("<{tag}>{content}</{tag}>") }),
21 43 // Self-closing tag
22 44 tags.clone().prop_map(|tag| format!("<{tag}/>")),
23 45 // Nested tags