max / makenotwork
6 files changed,
+58 insertions,
-17 deletions
| @@ -31,3 +31,35 @@ tempfile = "3" | |||
| 31 | 31 | [[bin]] | |
| 32 | 32 | name = "substitute_dir" | |
| 33 | 33 | required-features = ["assumptions"] | |
| 34 | + | ||
| 35 | + | [lints.rust] | |
| 36 | + | unused = "warn" | |
| 37 | + | unreachable_pub = "warn" | |
| 38 | + | ||
| 39 | + | [lints.clippy] | |
| 40 | + | pedantic = { level = "warn", priority = -1 } | |
| 41 | + | # Allow-list tuned from a measured breakdown across server/multithreaded/pter | |
| 42 | + | # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything | |
| 43 | + | # else in `pedantic` stays a warning. Keep this block identical across repos. | |
| 44 | + | module_name_repetitions = "allow" | |
| 45 | + | # Doc lints — no docs-completeness push underway. | |
| 46 | + | missing_errors_doc = "allow" | |
| 47 | + | missing_panics_doc = "allow" | |
| 48 | + | doc_markdown = "allow" | |
| 49 | + | # Numeric casts — endemic and mostly intentional in size/byte math. | |
| 50 | + | cast_possible_truncation = "allow" | |
| 51 | + | cast_sign_loss = "allow" | |
| 52 | + | cast_precision_loss = "allow" | |
| 53 | + | cast_possible_wrap = "allow" | |
| 54 | + | cast_lossless = "allow" | |
| 55 | + | # Subjective structure/style nags — high churn, low signal. | |
| 56 | + | must_use_candidate = "allow" | |
| 57 | + | too_many_lines = "allow" | |
| 58 | + | struct_excessive_bools = "allow" | |
| 59 | + | similar_names = "allow" | |
| 60 | + | items_after_statements = "allow" | |
| 61 | + | single_match_else = "allow" | |
| 62 | + | # Frequent false-positives in TUI/router-heavy code — added from the buckets breakdown. | |
| 63 | + | match_same_arms = "allow" | |
| 64 | + | unnecessary_wraps = "allow" | |
| 65 | + | type_complexity = "allow" |
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | /// Strip inline code (backtick) and fenced code blocks, replacing with spaces. | |
| 2 | 2 | #[cfg_attr(not(any(feature = "mentions", test)), allow(dead_code))] | |
| 3 | - | pub fn strip_code_spans(input: &str) -> String { | |
| 3 | + | pub(crate) fn strip_code_spans(input: &str) -> String { | |
| 4 | 4 | let mut out = String::with_capacity(input.len()); | |
| 5 | 5 | let mut chars = input.chars().peekable(); | |
| 6 | 6 | ||
| @@ -37,7 +37,7 @@ pub fn strip_code_spans(input: &str) -> String { | |||
| 37 | 37 | } | |
| 38 | 38 | ||
| 39 | 39 | /// Return byte ranges of inline code spans and fenced code blocks. | |
| 40 | - | pub fn code_span_ranges(input: &str) -> Vec<(usize, usize)> { | |
| 40 | + | pub(crate) fn code_span_ranges(input: &str) -> Vec<(usize, usize)> { | |
| 41 | 41 | let mut ranges = Vec::new(); | |
| 42 | 42 | let bytes = input.as_bytes(); | |
| 43 | 43 | let len = bytes.len(); | |
| @@ -137,7 +137,7 @@ mod tests { | |||
| 137 | 137 | // Distinguishes `*` from `+` (3+2=5) and pins `+ skipped` vs `- skipped`. | |
| 138 | 138 | let result = strip_code_spans("```ab```"); | |
| 139 | 139 | let spaces = result.chars().filter(|c| *c == ' ').count(); | |
| 140 | - | assert_eq!(spaces, 9, "expected 3*2 + 3 = 9 spaces, got {:?}", result); | |
| 140 | + | assert_eq!(spaces, 9, "expected 3*2 + 3 = 9 spaces, got {result:?}"); | |
| 141 | 141 | } | |
| 142 | 142 | ||
| 143 | 143 | #[test] | |
| @@ -146,7 +146,7 @@ mod tests { | |||
| 146 | 146 | // Distinguishes `tick_count * 2` from `tick_count + 2` (3 vs 4). | |
| 147 | 147 | let result = strip_code_spans("`a`"); | |
| 148 | 148 | let spaces = result.chars().filter(|c| *c == ' ').count(); | |
| 149 | - | assert_eq!(spaces, 4, "expected 1*2 + 2 = 4 spaces, got {:?}", result); | |
| 149 | + | assert_eq!(spaces, 4, "expected 1*2 + 2 = 4 spaces, got {result:?}"); | |
| 150 | 150 | } | |
| 151 | 151 | ||
| 152 | 152 | #[test] |
| @@ -97,9 +97,8 @@ pub fn restrict_media_hosts(html: &str, allowed_host: &str) -> String { | |||
| 97 | 97 | .attribute_filter(move |element, attribute, value| { | |
| 98 | 98 | let is_media_src = matches!( | |
| 99 | 99 | (element, attribute), | |
| 100 | - | ("img", "src" | "srcset") | |
| 100 | + | ("img" | "source", "src" | "srcset") | |
| 101 | 101 | | ("video", "src" | "poster") | |
| 102 | - | | ("source", "src" | "srcset") | |
| 103 | 102 | | ("audio", "src") | |
| 104 | 103 | ); | |
| 105 | 104 | if is_media_src { |
| @@ -148,46 +148,55 @@ impl Renderer { | |||
| 148 | 148 | } | |
| 149 | 149 | } | |
| 150 | 150 | ||
| 151 | + | #[must_use] | |
| 151 | 152 | pub fn with_tables(mut self, enabled: bool) -> Self { | |
| 152 | 153 | self.tables = enabled; | |
| 153 | 154 | self | |
| 154 | 155 | } | |
| 155 | 156 | ||
| 157 | + | #[must_use] | |
| 156 | 158 | pub fn with_strikethrough(mut self, enabled: bool) -> Self { | |
| 157 | 159 | self.strikethrough = enabled; | |
| 158 | 160 | self | |
| 159 | 161 | } | |
| 160 | 162 | ||
| 163 | + | #[must_use] | |
| 161 | 164 | pub fn with_footnotes(mut self, enabled: bool) -> Self { | |
| 162 | 165 | self.footnotes = enabled; | |
| 163 | 166 | self | |
| 164 | 167 | } | |
| 165 | 168 | ||
| 169 | + | #[must_use] | |
| 166 | 170 | pub fn with_smart_punctuation(mut self, enabled: bool) -> Self { | |
| 167 | 171 | self.smart_punctuation = enabled; | |
| 168 | 172 | self | |
| 169 | 173 | } | |
| 170 | 174 | ||
| 175 | + | #[must_use] | |
| 171 | 176 | pub fn with_tasklists(mut self, enabled: bool) -> Self { | |
| 172 | 177 | self.tasklists = enabled; | |
| 173 | 178 | self | |
| 174 | 179 | } | |
| 175 | 180 | ||
| 181 | + | #[must_use] | |
| 176 | 182 | pub fn with_strip_images(mut self, enabled: bool) -> Self { | |
| 177 | 183 | self.strip_images = enabled; | |
| 178 | 184 | self | |
| 179 | 185 | } | |
| 180 | 186 | ||
| 187 | + | #[must_use] | |
| 181 | 188 | pub fn with_strip_raw_html(mut self, enabled: bool) -> Self { | |
| 182 | 189 | self.strip_raw_html = enabled; | |
| 183 | 190 | self | |
| 184 | 191 | } | |
| 185 | 192 | ||
| 193 | + | #[must_use] | |
| 186 | 194 | pub fn with_dangerous_scheme_filter(mut self, enabled: bool) -> Self { | |
| 187 | 195 | self.dangerous_scheme_filter = enabled; | |
| 188 | 196 | self | |
| 189 | 197 | } | |
| 190 | 198 | ||
| 199 | + | #[must_use] | |
| 191 | 200 | pub fn with_sanitize(mut self, preset: SanitizePreset) -> Self { | |
| 192 | 201 | self.sanitize = preset; | |
| 193 | 202 | self | |
| @@ -205,6 +214,7 @@ impl Renderer { | |||
| 205 | 214 | /// content — docs, your own long-form — not for arbitrary UGC. | |
| 206 | 215 | /// | |
| 207 | 216 | /// [`extract_toc`]: crate::extract_toc | |
| 217 | + | #[must_use] | |
| 208 | 218 | pub fn with_heading_ids(mut self, enabled: bool) -> Self { | |
| 209 | 219 | self.heading_ids = enabled; | |
| 210 | 220 | self | |
| @@ -370,7 +380,7 @@ mod tests { | |||
| 370 | 380 | fn permissive_smart_punctuation() { | |
| 371 | 381 | let r = Renderer::permissive(); | |
| 372 | 382 | let html = r.render("It's a \"test\""); | |
| 373 | - | assert!(html.contains('\u{201c}') || html.contains('\u{201d}') || html.contains("\"")); | |
| 383 | + | assert!(html.contains('\u{201c}') || html.contains('\u{201d}') || html.contains('"')); | |
| 374 | 384 | } | |
| 375 | 385 | ||
| 376 | 386 | #[test] | |
| @@ -423,13 +433,11 @@ mod tests { | |||
| 423 | 433 | .render("hello <u>raw</u> world"); | |
| 424 | 434 | assert!( | |
| 425 | 435 | kept.contains("<u>"), | |
| 426 | - | "<u> should survive permissive: {}", | |
| 427 | - | kept | |
| 436 | + | "<u> should survive permissive: {kept}" | |
| 428 | 437 | ); | |
| 429 | 438 | assert!( | |
| 430 | 439 | !stripped.contains("<u>"), | |
| 431 | - | "<u> should be stripped: {}", | |
| 432 | - | stripped | |
| 440 | + | "<u> should be stripped: {stripped}" | |
| 433 | 441 | ); | |
| 434 | 442 | } | |
| 435 | 443 |
| @@ -26,7 +26,7 @@ pub(crate) fn permissive_builder() -> ammonia::Builder<'static> { | |||
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | 28 | impl SanitizePreset { | |
| 29 | - | pub(crate) fn clean(&self, html: &str) -> String { | |
| 29 | + | pub(crate) fn clean(self, html: &str) -> String { | |
| 30 | 30 | self.clean_with(html, false) | |
| 31 | 31 | } | |
| 32 | 32 | ||
| @@ -40,7 +40,7 @@ impl SanitizePreset { | |||
| 40 | 40 | /// about when that is safe. | |
| 41 | 41 | /// | |
| 42 | 42 | /// [`Renderer::with_heading_ids`]: crate::Renderer::with_heading_ids | |
| 43 | - | pub(crate) fn clean_with(&self, html: &str, allow_heading_ids: bool) -> String { | |
| 43 | + | pub(crate) fn clean_with(self, html: &str, allow_heading_ids: bool) -> String { | |
| 44 | 44 | let mut builder = match self { | |
| 45 | 45 | SanitizePreset::Permissive | SanitizePreset::Standard => permissive_builder(), | |
| 46 | 46 | SanitizePreset::Strict => { | |
| @@ -191,7 +191,7 @@ mod tests { | |||
| 191 | 191 | // SVG is a script-carrier (`<svg><script>` / animated `<set>`); ammonia's | |
| 192 | 192 | // default allowlist excludes it. Pin that so a default change can't admit | |
| 193 | 193 | // an SVG XSS vector into creator long-form content. | |
| 194 | - | let html = r#"<p>x</p><svg><script>alert(1)</script></svg>"#; | |
| 194 | + | let html = r"<p>x</p><svg><script>alert(1)</script></svg>"; | |
| 195 | 195 | let result = SanitizePreset::Permissive.clean(html); | |
| 196 | 196 | assert!(!result.contains("<svg"), "svg must be stripped: {result}"); | |
| 197 | 197 | assert!( |
| @@ -1,5 +1,6 @@ | |||
| 1 | 1 | use std::collections::HashMap; | |
| 2 | 2 | use std::collections::hash_map::Entry; | |
| 3 | + | use std::fmt::Write as _; | |
| 3 | 4 | ||
| 4 | 5 | use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd}; | |
| 5 | 6 | ||
| @@ -89,12 +90,13 @@ pub fn render_toc_html(entries: &[TocEntry]) -> String { | |||
| 89 | 90 | } | |
| 90 | 91 | let mut html = String::from("<nav class=\"toc\"><ul>\n"); | |
| 91 | 92 | for entry in entries { | |
| 92 | - | html.push_str(&format!( | |
| 93 | - | "<li class=\"toc-h{}\"><a href=\"#{}\">{}</a></li>\n", | |
| 93 | + | let _ = writeln!( | |
| 94 | + | html, | |
| 95 | + | "<li class=\"toc-h{}\"><a href=\"#{}\">{}</a></li>", | |
| 94 | 96 | entry.level, | |
| 95 | 97 | html_escape(&entry.anchor), | |
| 96 | 98 | html_escape(&entry.text), | |
| 97 | - | )); | |
| 99 | + | ); | |
| 98 | 100 | } | |
| 99 | 101 | html.push_str("</ul></nav>"); | |
| 100 | 102 | html |