max / pter
8 files changed,
+81 insertions,
-47 deletions
| @@ -18,3 +18,35 @@ criterion = { version = "0.8", features = ["html_reports"] } | |||
| 18 | 18 | [[bench]] | |
| 19 | 19 | name = "convert_bench" | |
| 20 | 20 | harness = false | |
| 21 | + | ||
| 22 | + | [lints.rust] | |
| 23 | + | unused = "warn" | |
| 24 | + | unreachable_pub = "warn" | |
| 25 | + | ||
| 26 | + | [lints.clippy] | |
| 27 | + | pedantic = { level = "warn", priority = -1 } | |
| 28 | + | # Allow-list tuned from a measured breakdown across server/multithreaded/pter | |
| 29 | + | # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything | |
| 30 | + | # else in `pedantic` stays a warning. Keep this block identical across repos. | |
| 31 | + | module_name_repetitions = "allow" | |
| 32 | + | # Doc lints — no docs-completeness push underway. | |
| 33 | + | missing_errors_doc = "allow" | |
| 34 | + | missing_panics_doc = "allow" | |
| 35 | + | doc_markdown = "allow" | |
| 36 | + | # Numeric casts — endemic and mostly intentional in size/byte math. | |
| 37 | + | cast_possible_truncation = "allow" | |
| 38 | + | cast_sign_loss = "allow" | |
| 39 | + | cast_precision_loss = "allow" | |
| 40 | + | cast_possible_wrap = "allow" | |
| 41 | + | cast_lossless = "allow" | |
| 42 | + | # Subjective structure/style nags — high churn, low signal. | |
| 43 | + | must_use_candidate = "allow" | |
| 44 | + | too_many_lines = "allow" | |
| 45 | + | struct_excessive_bools = "allow" | |
| 46 | + | similar_names = "allow" | |
| 47 | + | items_after_statements = "allow" | |
| 48 | + | single_match_else = "allow" | |
| 49 | + | # Frequent false-positives in TUI/router-heavy code across the ecosystem. | |
| 50 | + | match_same_arms = "allow" | |
| 51 | + | unnecessary_wraps = "allow" | |
| 52 | + | type_complexity = "allow" |
| @@ -1,4 +1,5 @@ | |||
| 1 | 1 | use criterion::{Criterion, criterion_group, criterion_main}; | |
| 2 | + | use std::fmt::Write as _; | |
| 2 | 3 | use std::hint::black_box; | |
| 3 | 4 | ||
| 4 | 5 | fn simple_email() -> &'static str { | |
| @@ -59,7 +60,7 @@ fn large_email() -> String { | |||
| 59 | 60 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>"; | |
| 60 | 61 | let mut html = String::from("<html><body>"); | |
| 61 | 62 | for i in 0..100 { | |
| 62 | - | html.push_str(&format!("<h3>Section {}</h3>", i)); | |
| 63 | + | let _ = write!(html, "<h3>Section {i}</h3>"); | |
| 63 | 64 | html.push_str(paragraph); | |
| 64 | 65 | } | |
| 65 | 66 | html.push_str("</body></html>"); | |
| @@ -69,28 +70,28 @@ fn large_email() -> String { | |||
| 69 | 70 | fn bench_simple(c: &mut Criterion) { | |
| 70 | 71 | let html = simple_email(); | |
| 71 | 72 | c.bench_function("simple_email", |b| { | |
| 72 | - | b.iter(|| pter::convert(black_box(html))) | |
| 73 | + | b.iter(|| pter::convert(black_box(html))); | |
| 73 | 74 | }); | |
| 74 | 75 | } | |
| 75 | 76 | ||
| 76 | 77 | fn bench_newsletter(c: &mut Criterion) { | |
| 77 | 78 | let html = newsletter_email(); | |
| 78 | 79 | c.bench_function("newsletter_layout_tables", |b| { | |
| 79 | - | b.iter(|| pter::convert(black_box(html))) | |
| 80 | + | b.iter(|| pter::convert(black_box(html))); | |
| 80 | 81 | }); | |
| 81 | 82 | } | |
| 82 | 83 | ||
| 83 | 84 | fn bench_reply_chain(c: &mut Criterion) { | |
| 84 | 85 | let html = reply_chain(); | |
| 85 | 86 | c.bench_function("reply_chain_nested", |b| { | |
| 86 | - | b.iter(|| pter::convert(black_box(html))) | |
| 87 | + | b.iter(|| pter::convert(black_box(html))); | |
| 87 | 88 | }); | |
| 88 | 89 | } | |
| 89 | 90 | ||
| 90 | 91 | fn bench_large(c: &mut Criterion) { | |
| 91 | 92 | let html = large_email(); | |
| 92 | 93 | c.bench_function("large_100_sections", |b| { | |
| 93 | - | b.iter(|| pter::convert(black_box(&html))) | |
| 94 | + | b.iter(|| pter::convert(black_box(&html))); | |
| 94 | 95 | }); | |
| 95 | 96 | } | |
| 96 | 97 |
| @@ -246,12 +246,11 @@ fn handle_block(el: ElementRef, ctx: &mut Context, kind: BlockKind) { | |||
| 246 | 246 | ||
| 247 | 247 | // Determine bullet or number | |
| 248 | 248 | let marker = match ctx.list_stack.last_mut() { | |
| 249 | - | Some(ListType::Unordered) => "- ".to_string(), | |
| 250 | 249 | Some(ListType::Ordered(n)) => { | |
| 251 | 250 | *n += 1; | |
| 252 | 251 | format!("{}. ", *n) | |
| 253 | 252 | } | |
| 254 | - | None => "- ".to_string(), | |
| 253 | + | Some(ListType::Unordered) | None => "- ".to_string(), | |
| 255 | 254 | }; | |
| 256 | 255 | ctx.push(&marker); | |
| 257 | 256 | walk_children(el, ctx); | |
| @@ -742,7 +741,7 @@ mod tests { | |||
| 742 | 741 | </td></tr></table>"; | |
| 743 | 742 | let md = convert(html); | |
| 744 | 743 | assert!(md.contains("Inner content")); | |
| 745 | - | assert!(!md.contains("|")); | |
| 744 | + | assert!(!md.contains('|')); | |
| 746 | 745 | } | |
| 747 | 746 | ||
| 748 | 747 | #[test] | |
| @@ -750,7 +749,7 @@ mod tests { | |||
| 750 | 749 | let html = r#"<table role="presentation"><tr><td>Content</td><td> </td></tr></table>"#; | |
| 751 | 750 | let md = convert(html); | |
| 752 | 751 | assert!(md.contains("Content")); | |
| 753 | - | assert!(!md.contains("|")); | |
| 752 | + | assert!(!md.contains('|')); | |
| 754 | 753 | } | |
| 755 | 754 | ||
| 756 | 755 | #[test] |
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | use scraper::node::Element; | |
| 2 | 2 | ||
| 3 | 3 | /// What kind of markdown wrapper an element produces. | |
| 4 | - | pub enum ElementAction { | |
| 4 | + | pub(crate) enum ElementAction { | |
| 5 | 5 | /// Skip this element and all its children entirely. | |
| 6 | 6 | Skip, | |
| 7 | 7 | /// Render children only, no wrapper (transparent element). | |
| @@ -12,7 +12,8 @@ pub enum ElementAction { | |||
| 12 | 12 | Inline(InlineKind), | |
| 13 | 13 | } | |
| 14 | 14 | ||
| 15 | - | pub enum BlockKind { | |
| 15 | + | #[derive(Clone, Copy)] | |
| 16 | + | pub(crate) enum BlockKind { | |
| 16 | 17 | Paragraph, | |
| 17 | 18 | Heading(u8), | |
| 18 | 19 | Blockquote, | |
| @@ -25,7 +26,8 @@ pub enum BlockKind { | |||
| 25 | 26 | Div, | |
| 26 | 27 | } | |
| 27 | 28 | ||
| 28 | - | pub enum InlineKind { | |
| 29 | + | #[derive(Clone, Copy)] | |
| 30 | + | pub(crate) enum InlineKind { | |
| 29 | 31 | Bold, | |
| 30 | 32 | Italic, | |
| 31 | 33 | Strikethrough, | |
| @@ -38,7 +40,7 @@ pub enum InlineKind { | |||
| 38 | 40 | } | |
| 39 | 41 | ||
| 40 | 42 | /// Classify an HTML element into the action pter should take. | |
| 41 | - | pub fn classify(el: &Element) -> ElementAction { | |
| 43 | + | pub(crate) fn classify(el: &Element) -> ElementAction { | |
| 42 | 44 | match el.name() { | |
| 43 | 45 | // Skip entirely | |
| 44 | 46 | "script" | "style" | "head" | "meta" | "link" | "title" | "noscript" => ElementAction::Skip, | |
| @@ -83,7 +85,7 @@ pub fn classify(el: &Element) -> ElementAction { | |||
| 83 | 85 | ||
| 84 | 86 | /// Check if an <img> element is a tracking pixel. | |
| 85 | 87 | /// Returns true if it should be skipped. | |
| 86 | - | pub fn is_tracking_pixel(el: &Element) -> bool { | |
| 88 | + | pub(crate) fn is_tracking_pixel(el: &Element) -> bool { | |
| 87 | 89 | let width = el.attr("width"); | |
| 88 | 90 | let height = el.attr("height"); | |
| 89 | 91 | ||
| @@ -128,7 +130,7 @@ pub fn is_tracking_pixel(el: &Element) -> bool { | |||
| 128 | 130 | /// | |
| 129 | 131 | /// Catches display:none, visibility:hidden, and spacer tricks | |
| 130 | 132 | /// like font-size:0 or line-height:0 (commonly used in email templates). | |
| 131 | - | pub fn is_hidden(el: &Element) -> bool { | |
| 133 | + | pub(crate) fn is_hidden(el: &Element) -> bool { | |
| 132 | 134 | if let Some(style) = el.attr("style") { | |
| 133 | 135 | let s = style.to_lowercase(); | |
| 134 | 136 | if s.contains("display:none") |
| @@ -10,7 +10,7 @@ use scraper::node::Node; | |||
| 10 | 10 | /// An element is a reply boundary if it's a container that wraps quoted | |
| 11 | 11 | /// content from a previous message in the thread. The converter treats | |
| 12 | 12 | /// these identically to `<blockquote>` — children get `>` prefixed. | |
| 13 | - | pub fn is_reply_boundary(el: ElementRef) -> bool { | |
| 13 | + | pub(crate) fn is_reply_boundary(el: ElementRef) -> bool { | |
| 14 | 14 | let element = el.value(); | |
| 15 | 15 | let name = element.name(); | |
| 16 | 16 | ||
| @@ -44,7 +44,7 @@ pub fn is_reply_boundary(el: ElementRef) -> bool { | |||
| 44 | 44 | /// | |
| 45 | 45 | /// Returns the attribution text (e.g. "On Mon, Jan 5, Alice wrote:") if found, | |
| 46 | 46 | /// so the converter can render it above the quoted block. | |
| 47 | - | pub fn find_attribution(el: ElementRef) -> Option<String> { | |
| 47 | + | pub(crate) fn find_attribution(el: ElementRef) -> Option<String> { | |
| 48 | 48 | // Check the element's own leading text for attribution patterns | |
| 49 | 49 | for child in el.children() { | |
| 50 | 50 | match child.value() { | |
| @@ -190,7 +190,7 @@ fn previous_sibling_text(el: ElementRef) -> Option<String> { | |||
| 190 | 190 | /// | |
| 191 | 191 | /// This catches `<hr>` or styled divs that act as visual separators | |
| 192 | 192 | /// before reply content (common in Outlook "From: ... Sent: ..." blocks). | |
| 193 | - | pub fn is_outlook_separator(el: ElementRef) -> bool { | |
| 193 | + | pub(crate) fn is_outlook_separator(el: ElementRef) -> bool { | |
| 194 | 194 | let element = el.value(); | |
| 195 | 195 | ||
| 196 | 196 | // Outlook uses a specific pattern: a div containing | |
| @@ -431,7 +431,7 @@ mod tests { | |||
| 431 | 431 | ||
| 432 | 432 | #[test] | |
| 433 | 433 | fn find_attribution_in_leading_text() { | |
| 434 | - | let html = r#"<div>On Mon, Alice wrote:<blockquote>quoted</blockquote></div>"#; | |
| 434 | + | let html = r"<div>On Mon, Alice wrote:<blockquote>quoted</blockquote></div>"; | |
| 435 | 435 | let (doc, sel) = parse_and_select(html, "div"); | |
| 436 | 436 | let el = doc.select(&sel).next().unwrap(); | |
| 437 | 437 | let attr = find_attribution(el); | |
| @@ -441,7 +441,7 @@ mod tests { | |||
| 441 | 441 | ||
| 442 | 442 | #[test] | |
| 443 | 443 | fn find_attribution_none_when_no_match() { | |
| 444 | - | let html = r#"<div>Just regular text here, nothing fancy.</div>"#; | |
| 444 | + | let html = r"<div>Just regular text here, nothing fancy.</div>"; | |
| 445 | 445 | let (doc, sel) = parse_and_select(html, "div"); | |
| 446 | 446 | let el = doc.select(&sel).next().unwrap(); | |
| 447 | 447 | assert!(find_attribution(el).is_none()); | |
| @@ -452,7 +452,7 @@ mod tests { | |||
| 452 | 452 | // Element-then-text: the Text(_) arm should still match leading text BEFORE | |
| 453 | 453 | // hitting any element. With a leading element, the loop should `break` | |
| 454 | 454 | // out without inspecting later text. Catches "delete match arm Node::Element(_)". | |
| 455 | - | let html = r#"<div><span>hi</span>On Mon, Alice wrote:</div>"#; | |
| 455 | + | let html = r"<div><span>hi</span>On Mon, Alice wrote:</div>"; | |
| 456 | 456 | let (doc, sel) = parse_and_select(html, "div"); | |
| 457 | 457 | let el = doc.select(&sel).next().unwrap(); | |
| 458 | 458 | // Leading content is an element, not text — and the later text falls outside | |
| @@ -475,7 +475,7 @@ mod tests { | |||
| 475 | 475 | ||
| 476 | 476 | #[test] | |
| 477 | 477 | fn boundary_div_with_attribution_then_blockquote() { | |
| 478 | - | let html = r#"<div>On Mon, Alice wrote:<blockquote>quoted</blockquote></div>"#; | |
| 478 | + | let html = r"<div>On Mon, Alice wrote:<blockquote>quoted</blockquote></div>"; | |
| 479 | 479 | let (doc, sel) = parse_and_select(html, "div"); | |
| 480 | 480 | let el = doc.select(&sel).next().unwrap(); | |
| 481 | 481 | assert!(is_reply_boundary(el)); | |
| @@ -486,7 +486,7 @@ mod tests { | |||
| 486 | 486 | // A bare blockquote-wrapping div without attribution text is not a boundary. | |
| 487 | 487 | // Catches "replace has_attribution_then_quote -> bool with false" (would | |
| 488 | 488 | // make this still pass, but the positive case above would fail). | |
| 489 | - | let html = r#"<div><blockquote>quoted</blockquote></div>"#; | |
| 489 | + | let html = r"<div><blockquote>quoted</blockquote></div>"; | |
| 490 | 490 | let (doc, sel) = parse_and_select(html, "div"); | |
| 491 | 491 | let el = doc.select(&sel).next().unwrap(); | |
| 492 | 492 | assert!(!is_reply_boundary(el)); | |
| @@ -496,7 +496,7 @@ mod tests { | |||
| 496 | 496 | fn boundary_div_attribution_no_blockquote_is_false() { | |
| 497 | 497 | // Attribution text but no blockquote → not a boundary. | |
| 498 | 498 | // Catches the L151 == mutation (would treat any element as blockquote). | |
| 499 | - | let html = r#"<div>On Mon, Alice wrote:<p>not a quote</p></div>"#; | |
| 499 | + | let html = r"<div>On Mon, Alice wrote:<p>not a quote</p></div>"; | |
| 500 | 500 | let (doc, sel) = parse_and_select(html, "div"); | |
| 501 | 501 | let el = doc.select(&sel).next().unwrap(); | |
| 502 | 502 | assert!(!is_reply_boundary(el)); | |
| @@ -506,7 +506,7 @@ mod tests { | |||
| 506 | 506 | fn boundary_div_attribution_br_blockquote() { | |
| 507 | 507 | // Attribution → <br> → blockquote. The <br> must be skipped. | |
| 508 | 508 | // Catches the L155 != mutation in br-handling. | |
| 509 | - | let html = r#"<div>On Mon, Alice wrote:<br><blockquote>quoted</blockquote></div>"#; | |
| 509 | + | let html = r"<div>On Mon, Alice wrote:<br><blockquote>quoted</blockquote></div>"; | |
| 510 | 510 | let (doc, sel) = parse_and_select(html, "div"); | |
| 511 | 511 | let el = doc.select(&sel).next().unwrap(); | |
| 512 | 512 | assert!(is_reply_boundary(el)); | |
| @@ -516,7 +516,7 @@ mod tests { | |||
| 516 | 516 | fn boundary_div_non_br_element_before_attribution_is_false() { | |
| 517 | 517 | // Non-br element BEFORE finding attribution → early return false. | |
| 518 | 518 | // Catches the L157 `!` deletion. | |
| 519 | - | let html = r#"<div><p>preface</p>On Mon, Alice wrote:<blockquote>q</blockquote></div>"#; | |
| 519 | + | let html = r"<div><p>preface</p>On Mon, Alice wrote:<blockquote>q</blockquote></div>"; | |
| 520 | 520 | let (doc, sel) = parse_and_select(html, "div"); | |
| 521 | 521 | let el = doc.select(&sel).next().unwrap(); | |
| 522 | 522 | assert!(!is_reply_boundary(el)); |
| @@ -10,7 +10,7 @@ use scraper::ElementRef; | |||
| 10 | 10 | /// - Has multiple rows where multiple cells contain substantive text | |
| 11 | 11 | /// | |
| 12 | 12 | /// Everything else is treated as a layout table and unwrapped. | |
| 13 | - | pub fn is_data_table(table: ElementRef) -> bool { | |
| 13 | + | pub(crate) fn is_data_table(table: ElementRef) -> bool { | |
| 14 | 14 | let el = table.value(); | |
| 15 | 15 | ||
| 16 | 16 | // role attribute | |
| @@ -70,7 +70,7 @@ fn has_substantive_text(el: ElementRef) -> bool { | |||
| 70 | 70 | /// | |
| 71 | 71 | /// Returns (headers, rows) where each is a Vec of cell text strings. | |
| 72 | 72 | /// If no `<thead>`/`<th>` row exists, the first row is used as headers. | |
| 73 | - | pub fn extract_table_data(table: ElementRef) -> (Vec<String>, Vec<Vec<String>>) { | |
| 73 | + | pub(crate) fn extract_table_data(table: ElementRef) -> (Vec<String>, Vec<Vec<String>>) { | |
| 74 | 74 | let mut headers: Vec<String> = Vec::new(); | |
| 75 | 75 | let mut rows: Vec<Vec<String>> = Vec::new(); | |
| 76 | 76 | ||
| @@ -140,7 +140,7 @@ fn has_th_cells(tr: ElementRef) -> bool { | |||
| 140 | 140 | } | |
| 141 | 141 | ||
| 142 | 142 | /// Render a data table as a GFM markdown table. | |
| 143 | - | pub fn render_markdown_table(headers: &[String], rows: &[Vec<String>]) -> String { | |
| 143 | + | pub(crate) fn render_markdown_table(headers: &[String], rows: &[Vec<String>]) -> String { | |
| 144 | 144 | if headers.is_empty() { | |
| 145 | 145 | return String::new(); | |
| 146 | 146 | } |
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | /// - Collapse runs of 3+ newlines into 2 (one blank line) | |
| 4 | 4 | /// - Trim leading/trailing whitespace | |
| 5 | 5 | /// - Remove trailing whitespace from each line | |
| 6 | - | pub fn normalize(input: &str) -> String { | |
| 6 | + | pub(crate) fn normalize(input: &str) -> String { | |
| 7 | 7 | let mut result = String::with_capacity(input.len()); | |
| 8 | 8 | let mut consecutive_newlines = 0u32; | |
| 9 | 9 | ||
| @@ -20,7 +20,7 @@ pub fn normalize(input: &str) -> String { | |||
| 20 | 20 | } | |
| 21 | 21 | ||
| 22 | 22 | // Trim trailing whitespace from each line | |
| 23 | - | let lines: Vec<&str> = result.lines().map(|l| l.trim_end()).collect(); | |
| 23 | + | let lines: Vec<&str> = result.lines().map(str::trim_end).collect(); | |
| 24 | 24 | let joined = lines.join("\n"); | |
| 25 | 25 | joined.trim().to_string() | |
| 26 | 26 | } |
| @@ -2,7 +2,7 @@ use pter::convert; | |||
| 2 | 2 | ||
| 3 | 3 | #[test] | |
| 4 | 4 | fn simple_email() { | |
| 5 | - | let html = r#" | |
| 5 | + | let html = r" | |
| 6 | 6 | <html> | |
| 7 | 7 | <head><title>Email</title></head> | |
| 8 | 8 | <body> | |
| @@ -12,7 +12,7 @@ fn simple_email() { | |||
| 12 | 12 | <p>Best,<br>Alice</p> | |
| 13 | 13 | </body> | |
| 14 | 14 | </html> | |
| 15 | - | "#; | |
| 15 | + | "; | |
| 16 | 16 | ||
| 17 | 17 | let md = convert(html); | |
| 18 | 18 | assert!(md.contains("# Meeting Tomorrow")); | |
| @@ -56,14 +56,14 @@ fn email_with_tracking_pixels() { | |||
| 56 | 56 | ||
| 57 | 57 | #[test] | |
| 58 | 58 | fn email_with_quoted_reply() { | |
| 59 | - | let html = r#" | |
| 59 | + | let html = r" | |
| 60 | 60 | <body> | |
| 61 | 61 | <p>Thanks, that works for me.</p> | |
| 62 | 62 | <blockquote> | |
| 63 | 63 | <p>Can we meet at 3pm instead?</p> | |
| 64 | 64 | </blockquote> | |
| 65 | 65 | </body> | |
| 66 | - | "#; | |
| 66 | + | "; | |
| 67 | 67 | ||
| 68 | 68 | let md = convert(html); | |
| 69 | 69 | assert!(md.contains("Thanks, that works for me.")); | |
| @@ -72,14 +72,14 @@ fn email_with_quoted_reply() { | |||
| 72 | 72 | ||
| 73 | 73 | #[test] | |
| 74 | 74 | fn email_with_signature_line() { | |
| 75 | - | let html = r#" | |
| 75 | + | let html = r" | |
| 76 | 76 | <body> | |
| 77 | 77 | <p>See you then.</p> | |
| 78 | 78 | <hr> | |
| 79 | 79 | <p>Alice Smith</p> | |
| 80 | 80 | <p>Engineering Lead</p> | |
| 81 | 81 | </body> | |
| 82 | - | "#; | |
| 82 | + | "; | |
| 83 | 83 | ||
| 84 | 84 | let md = convert(html); | |
| 85 | 85 | assert!(md.contains("See you then.")); | |
| @@ -89,7 +89,7 @@ fn email_with_signature_line() { | |||
| 89 | 89 | ||
| 90 | 90 | #[test] | |
| 91 | 91 | fn deeply_nested_blockquotes() { | |
| 92 | - | let html = r#" | |
| 92 | + | let html = r" | |
| 93 | 93 | <body> | |
| 94 | 94 | <p>Got it.</p> | |
| 95 | 95 | <blockquote> | |
| @@ -102,7 +102,7 @@ fn deeply_nested_blockquotes() { | |||
| 102 | 102 | </blockquote> | |
| 103 | 103 | </blockquote> | |
| 104 | 104 | </body> | |
| 105 | - | "#; | |
| 105 | + | "; | |
| 106 | 106 | ||
| 107 | 107 | let md = convert(html); | |
| 108 | 108 | assert!(md.contains("Got it.")); | |
| @@ -113,7 +113,7 @@ fn deeply_nested_blockquotes() { | |||
| 113 | 113 | ||
| 114 | 114 | #[test] | |
| 115 | 115 | fn complex_list_structure() { | |
| 116 | - | let html = r#" | |
| 116 | + | let html = r" | |
| 117 | 117 | <body> | |
| 118 | 118 | <p>Action items:</p> | |
| 119 | 119 | <ol> | |
| @@ -126,7 +126,7 @@ fn complex_list_structure() { | |||
| 126 | 126 | <li>Deploy to staging</li> | |
| 127 | 127 | </ol> | |
| 128 | 128 | </body> | |
| 129 | - | "#; | |
| 129 | + | "; | |
| 130 | 130 | ||
| 131 | 131 | let md = convert(html); | |
| 132 | 132 | assert!(md.contains("Action items:")); | |
| @@ -174,7 +174,7 @@ fn hidden_content_stripped() { | |||
| 174 | 174 | ||
| 175 | 175 | #[test] | |
| 176 | 176 | fn script_and_style_fully_removed() { | |
| 177 | - | let html = r#" | |
| 177 | + | let html = r" | |
| 178 | 178 | <html> | |
| 179 | 179 | <head> | |
| 180 | 180 | <style>body { color: red; }</style> | |
| @@ -185,7 +185,7 @@ fn script_and_style_fully_removed() { | |||
| 185 | 185 | <script>document.write('injected')</script> | |
| 186 | 186 | </body> | |
| 187 | 187 | </html> | |
| 188 | - | "#; | |
| 188 | + | "; | |
| 189 | 189 | ||
| 190 | 190 | let md = convert(html); | |
| 191 | 191 | assert_eq!(md, "Safe content"); | |
| @@ -234,7 +234,7 @@ fn newsletter_table_layout() { | |||
| 234 | 234 | ||
| 235 | 235 | #[test] | |
| 236 | 236 | fn data_table_preserved() { | |
| 237 | - | let html = r#" | |
| 237 | + | let html = r" | |
| 238 | 238 | <body> | |
| 239 | 239 | <p>Order summary:</p> | |
| 240 | 240 | <table> | |
| @@ -245,7 +245,7 @@ fn data_table_preserved() { | |||
| 245 | 245 | </tbody> | |
| 246 | 246 | </table> | |
| 247 | 247 | </body> | |
| 248 | - | "#; | |
| 248 | + | "; | |
| 249 | 249 | ||
| 250 | 250 | let md = convert(html); | |
| 251 | 251 | assert!(md.contains("Order summary:")); | |
| @@ -323,7 +323,7 @@ fn apple_mail_reply() { | |||
| 323 | 323 | ||
| 324 | 324 | #[test] | |
| 325 | 325 | fn outlook_reply_with_separator() { | |
| 326 | - | let html = r#" | |
| 326 | + | let html = r" | |
| 327 | 327 | <body> | |
| 328 | 328 | <div> | |
| 329 | 329 | <p>I'll handle it.</p> | |
| @@ -339,7 +339,7 @@ fn outlook_reply_with_separator() { | |||
| 339 | 339 | <p>Can you take a look at the report?</p> | |
| 340 | 340 | </div> | |
| 341 | 341 | </body> | |
| 342 | - | "#; | |
| 342 | + | "; | |
| 343 | 343 | ||
| 344 | 344 | let md = convert(html); | |
| 345 | 345 | assert!(md.contains("I'll handle it.")); |