@@ -230,9 +230,9 @@ fn collect_images_recursive(nodes: &[Node], hints: &mut Vec<crate::ast::PreloadH
230230 for node in nodes {
231231 if hints.len() >= max { return; }
232232 if let Node::Element(elem) = node {
233− if elem.kind == ElementKind::Image {
234− if let Some(ref src) = elem.argument {
235− if !src.is_empty()
233+ if elem.kind == ElementKind::Image
234+ && let Some(ref src) = elem.argument
235+ && !src.is_empty()
236236 && !src.starts_with("data:")
237237 && !src.starts_with('#')
238238 {
@@ -242,8 +242,6 @@ fn collect_images_recursive(nodes: &[Node], hints: &mut Vec<crate::ast::PreloadH
242242 crossorigin: false,
243243 });
244244 }
245− }
246− }
247245 collect_images_recursive(&elem.children, hints, max);
248246 }
249247 }
@@ -270,11 +268,9 @@ fn preprocess(input: &str) -> Vec<Line> {
270268 let indent = line.len() - line.trim_start().len();
271269
272270 // Handle @raw """..."""
273− if trimmed.starts_with("@raw") {
274− let after_raw = trimmed[4..].trim_start();
275− if after_raw.starts_with("\"\"\"") {
276− let after_open = &after_raw[3..];
277−
271+ if let Some(raw_rest) = trimmed.strip_prefix("@raw") {
272+ let after_raw = raw_rest.trim_start();
273+ if let Some(after_open) = after_raw.strip_prefix("\"\"\"") {
278274 // Single-line: @raw """content"""
279275 if after_open.ends_with("\"\"\"") && after_open.len() >= 3 {
280276 let content = &after_open[..after_open.len() - 3];
@@ -443,8 +439,8 @@ impl Parser {
443439 if let Some((name, value)) = rest.split_once(' ') {
444440 let value = value.trim();
445441 // Support @let name = expr syntax (strip leading =)
446− let value = if value.starts_with("= ") {
447− value[2..].trim()
442+ let value = if let Some(after_eq) = value.strip_prefix("= ") {
443+ after_eq.trim()
448444 } else if value == "=" {
449445 ""
450446 } else {
@@ -453,8 +449,7 @@ impl Parser {
453449
454450 // Multi-line @let with triple quotes: @let name """..."""
455451 let value_str;
456− let value = if value.starts_with("\"\"\"") {
457− let after_open = &value[3..];
452+ let value = if let Some(after_open) = value.strip_prefix("\"\"\"") {
458453 if after_open.ends_with("\"\"\"") && after_open.len() >= 3 {
459454 // Single-line triple-quote: @let name """value"""
460455 value_str = after_open[..after_open.len()-3].to_string();
@@ -1024,11 +1019,10 @@ impl Parser {
10241019 .cloned()
10251020 .collect();
10261021 for name in new_var_keys {
1027− if let Some(val) = ctx.variables.remove(&name) {
1028− if !name.starts_with("__") {
1022+ if let Some(val) = ctx.variables.remove(&name)
1023+ && !name.starts_with("__") {
10291024 ctx.variables.insert(format!("{}.{}", prefix, name), val);
10301025 }
1031− }
10321026 }
10331027 } else {
10341028 // Parse the file but discard DOM nodes — only keep definitions
@@ -1063,8 +1057,8 @@ impl Parser {
10631057 (rest, None)
10641058 };
10651059 let env_val = std::env::var(var_name).ok().or_else(|| default_val.map(|d| {
1066− let d = substitute_vars(d, &ctx.variables);
1067− d
1060+
1061+ substitute_vars(d, &ctx.variables)
10681062 }));
10691063 match env_val {
10701064 Some(val) => {
@@ -1357,10 +1351,10 @@ impl Parser {
13571351 if let Some(rest) = content.strip_prefix("@use ") {
13581352 let rest = rest.trim();
13591353 // @use "./file.hl" fn1, fn2, define1
1360− let (filename, names_str) = if rest.starts_with('"') {
1361− if let Some(end_quote) = rest[1..].find('"') {
1362− let filename = &rest[1..end_quote + 1];
1363− let names = rest[end_quote + 2..].trim();
1354+ let (filename, names_str) = if let Some(after_quote) = rest.strip_prefix('"') {
1355+ if let Some(end_quote) = after_quote.find('"') {
1356+ let filename = &after_quote[..end_quote];
1357+ let names = after_quote[end_quote + 1..].trim();
13641358 (filename.to_string(), names.to_string())
13651359 } else {
13661360 return Err(ParseError {
@@ -1529,14 +1523,13 @@ impl Parser {
15291523 ctx.variables.insert(var_name.clone(), stems.join(" "));
15301524 // Also parse each JSON file and store keys as prefix_stem_key
15311525 for (stem, text) in &items {
1532− if let Some(json) = parse_json(text.trim()) {
1533− if let JsonValue::Object(ref obj) = json {
1526+ if let Some(json) = parse_json(text.trim())
1527+ && let JsonValue::Object(ref obj) = json {
15341528 for (key, val) in obj {
15351529 let var_key = format!("{}_{}", stem, key);
15361530 ctx.variables.insert(var_key, json_value_to_string(val));
15371531 }
15381532 }
1539− }
15401533 }
15411534
15421535 return Ok(None);
@@ -1734,7 +1727,7 @@ impl Parser {
17341727 .and_then(|v| v.parse().ok())
17351728 .unwrap_or(1);
17361729 let total_items = items.len();
1737− let total_pages = (total_items + size - 1) / size;
1730+ let total_pages = total_items.div_ceil(size);
17381731 let start = (current_page - 1) * size;
17391732 let end = (start + size).min(total_items);
17401733 let page_items: Vec<String> = if start < total_items {
@@ -1757,17 +1750,15 @@ impl Parser {
17571750
17581751 // Check for @else block (empty-state fallback)
17591752 let mut else_lines = Vec::new();
1760− if self.pos < self.lines.len() && self.lines[self.pos].indent == current_indent {
1761− if let LineContent::Normal(ref s) = self.lines[self.pos].content {
1762− if s.trim() == "@else" {
1753+ if self.pos < self.lines.len() && self.lines[self.pos].indent == current_indent
1754+ && let LineContent::Normal(ref s) = self.lines[self.pos].content
1755+ && s.trim() == "@else" {
17631756 self.pos += 1; // consume @else
17641757 while self.pos < self.lines.len() && self.lines[self.pos].indent > current_indent {
17651758 else_lines.push(self.lines[self.pos].clone());
17661759 self.pos += 1;
17671760 }
17681761 }
1769− }
1770− }
17711762
17721763 if body_lines.is_empty() {
17731764 return Ok(None);
@@ -1817,7 +1808,7 @@ impl Parser {
18171808 }
18181809
18191810 let has_extra_vars = var_names.len() > 2
1820− || (var_names.len() == 2 && items.first().map_or(false, |it| it.contains(' ')));
1811+ || (var_names.len() == 2 && items.first().is_some_and(|it| it.contains(' ')));
18211812
18221813 for (i, item) in items.iter().enumerate() {
18231814 // Always expose $_index for the current iteration
@@ -2018,9 +2009,7 @@ impl Parser {
20182009 };
20192010
20202011 // Also emit the IntersectionObserver script as a sibling raw node
2021− let script = Node::Raw(format!(
2022− "<script>(function(){{var d=document.querySelectorAll('[data-hl-defer]');if(!d.length)return;var o=new IntersectionObserver(function(e){{e.forEach(function(i){{if(i.isIntersecting){{i.target.removeAttribute('hidden');i.target.classList.remove('hl-defer-placeholder');o.unobserve(i.target)}}}});}},{{rootMargin:'200px'}});d.forEach(function(el){{o.observe(el)}})}})()</script>"
2023− ));
2012+ let script = Node::Raw("<script>(function(){var d=document.querySelectorAll('[data-hl-defer]');if(!d.length)return;var o=new IntersectionObserver(function(e){e.forEach(function(i){if(i.isIntersecting){i.target.removeAttribute('hidden');i.target.classList.remove('hl-defer-placeholder');o.unobserve(i.target)}});},{rootMargin:'200px'});d.forEach(function(el){o.observe(el)})})()</script>".to_string());
20242013
20252014 return Ok(Some(vec![Node::Element(wrapper), script]));
20262015 }
@@ -2410,7 +2399,7 @@ impl Parser {
24102399 let value = substitute_vars(value.trim(), &ctx.variables);
24112400 ctx.translations
24122401 .entry(current_locale.clone())
2413− .or_insert_with(HashMap::new)
2402+ .or_default()
24142403 .insert(key, value);
24152404 }
24162405 }
@@ -2435,16 +2424,14 @@ impl Parser {
24352424 if let Some(rest) = content.strip_prefix("@deprecated ") {
24362425 let message = rest.trim().to_string();
24372426 // Peek at the next line to get the function name
2438− if self.pos < self.lines.len() {
2439− if let LineContent::Normal(ref s) = self.lines[self.pos].content {
2440− if let Some(fn_rest) = s.trim().strip_prefix("@fn ") {
2427+ if self.pos < self.lines.len()
2428+ && let LineContent::Normal(ref s) = self.lines[self.pos].content
2429+ && let Some(fn_rest) = s.trim().strip_prefix("@fn ") {
24412430 let parts: Vec<&str> = fn_rest.split_whitespace().collect();
24422431 if let Some(&fn_name) = parts.first() {
24432432 ctx.deprecated_fns.insert(fn_name.to_string(), message);
24442433 }
24452434 }
2446− }
2447− }
24482435 return Ok(None);
24492436 }
24502437
@@ -2640,14 +2627,13 @@ impl Parser {
26402627 let mut in_style = false;
26412628 let mut style_indent = 0;
26422629 while self.pos < self.lines.len() && self.lines[self.pos].indent > current_indent {
2643− if let LineContent::Normal(ref s) = self.lines[self.pos].content {
2644− if s.trim() == "@style" {
2630+ if let LineContent::Normal(ref s) = self.lines[self.pos].content
2631+ && s.trim() == "@style" {
26452632 in_style = true;
26462633 style_indent = self.lines[self.pos].indent;
26472634 self.pos += 1;
26482635 continue;
26492636 }
2650− }
26512637 if in_style && self.lines[self.pos].indent > style_indent {
26522638 style_lines.push(self.lines[self.pos].clone());
26532639 } else {
@@ -2805,14 +2791,12 @@ impl Parser {
28052791 let mut slot_contents: HashMap<String, Vec<Node>> = HashMap::new();
28062792 let mut caller_children = Vec::new();
28072793 for child in all_caller_children {
2808− if let Node::Element(ref elem) = child {
2809− if let ElementKind::Slot(ref slot_name) = elem.kind {
2810− if !slot_name.is_empty() {
2794+ if let Node::Element(ref elem) = child
2795+ && let ElementKind::Slot(ref slot_name) = elem.kind
2796+ && !slot_name.is_empty() {
28112797 slot_contents.entry(slot_name.clone()).or_default().extend(elem.children.clone());
28122798 continue;
28132799 }
2814− }
2815− }
28162800 caller_children.push(child);
28172801 }
28182802
@@ -2956,7 +2940,7 @@ fn replace_children_and_slots(
29562940
29572941fn extract_element_name(content: &str) -> &str {
29582942 let without_at = &content[1..];
2959− match without_at.find(|c: char| c == ' ' || c == '[') {
2943+ match without_at.find([' ', '[']) {
29602944 Some(i) => &without_at[..i],
29612945 None => without_at,
29622946 }
@@ -2970,9 +2954,8 @@ fn parse_single_element(
29702954 let (kind, rest) = if content.starts_with('[') {
29712955 // Implicit @el
29722956 (ElementKind::El, content.to_string())
2973− } else if content.starts_with('@') {
2974− let without_at = &content[1..];
2975− match without_at.find(|c: char| c == ' ' || c == '[') {
2957+ } else if let Some(without_at) = content.strip_prefix('@') {
2958+ match without_at.find([' ', '[']) {
29762959 Some(i) => {
29772960 let kind_str = &without_at[..i];
29782961 let rest = if without_at.as_bytes()[i] == b'[' {
@@ -3313,8 +3296,8 @@ fn check_undefined_vars(input: &str, vars: &HashMap<String, String>, line_num: u
33133296 end -= 1;
33143297 }
33153298 let name: String = chars[start..end].iter().collect();
3316− if !name.is_empty() && !vars.contains_key(&name) {
3317− if let Some(closest) = suggest_var_name(&name, vars) {
3299+ if !name.is_empty() && !vars.contains_key(&name)
3300+ && let Some(closest) = suggest_var_name(&name, vars) {
33183301 warnings.push(Diagnostic {
33193302 line: line_num,
33203303 column: Some(col),
@@ -3323,7 +3306,6 @@ fn check_undefined_vars(input: &str, vars: &HashMap<String, String>, line_num: u
33233306 source_line: Some(input.to_string()),
33243307 });
33253308 }
3326− }
33273309 i = end;
33283310 } else {
33293311 i += 1;
@@ -3777,8 +3759,7 @@ fn parse_attr_list(input: &str, line_num: usize, ctx: &mut ParseContext, validat
37773759 }
37783760
37793761 // ...$name spread — expand mixin or define attributes
3780− if part.starts_with("...$") {
3781− let name = &part[4..];
3762+ if let Some(name) = part.strip_prefix("...$") {
37823763 if let Some(mixin_attrs) = ctx.mixins.get(name) {
37833764 ctx.used_mixins.insert(name.to_string());
37843765 attrs.extend(mixin_attrs.clone());
@@ -3792,8 +3773,7 @@ fn parse_attr_list(input: &str, line_num: usize, ctx: &mut ParseContext, validat
37923773 }
37933774
37943775 // $define reference — expand
3795− if part.starts_with('$') {
3796− let name = &part[1..];
3776+ if let Some(name) = part.strip_prefix('$') {
37973777 if let Some(define_attrs) = ctx.defines.get(name) {
37983778 ctx.used_defines.insert(name.to_string());
37993779 attrs.extend(define_attrs.clone());
@@ -3877,9 +3857,9 @@ fn parse_attr_list(input: &str, line_num: usize, ctx: &mut ParseContext, validat
38773857 }
38783858
38793859 // Color validation for hex colors
3880− if matches!(strip_all_prefixes(&attr.key), "background" | "color") {
3881− if let Some(ref val) = attr.value {
3882− if val.starts_with('#') && !is_valid_hex_color(val) {
3860+ if matches!(strip_all_prefixes(&attr.key), "background" | "color")
3861+ && let Some(ref val) = attr.value
3862+ && val.starts_with('#') && !is_valid_hex_color(val) {
38833863 ctx.diagnostics.push(Diagnostic {
38843864 line: line_num,
38853865 column: None,
@@ -3888,8 +3868,6 @@ fn parse_attr_list(input: &str, line_num: usize, ctx: &mut ParseContext, validat
38883868 source_line: None,
38893869 });
38903870 }
3891− }
3892− }
38933871 }
38943872
38953873 // Warn on unknown attributes
@@ -4251,8 +4229,7 @@ fn strip_all_prefixes(key: &str) -> &str {
42514229 .or_else(|| key.strip_prefix("invalid:"))
42524230 .or_else(|| {
42534231 // Handle nth:EXPR: prefix (e.g., nth:3:background -> background)
4254− if key.starts_with("nth:") {
4255− let rest = &key[4..];
4232+ if let Some(rest) = key.strip_prefix("nth:") {
42564233 rest.find(':').map(|pos| &rest[pos + 1..])
42574234 } else {
42584235 None
@@ -4288,13 +4265,13 @@ fn strip_all_prefixes(key: &str) -> &str {
42884265 .or_else(|| key.strip_prefix("portrait:"))
42894266 .unwrap_or(key);
42904267 // Strip container query prefixes (cq-sm:, cq-md:, etc.)
4291− let key = key.strip_prefix("cq-sm:")
4268+
4269+ (key.strip_prefix("cq-sm:")
42924270 .or_else(|| key.strip_prefix("cq-md:"))
42934271 .or_else(|| key.strip_prefix("cq-lg:"))
42944272 .or_else(|| key.strip_prefix("cq-xl:"))
42954273 .or_else(|| key.strip_prefix("cq-2xl:"))
4296− .unwrap_or(key);
4297− key
4274+ .unwrap_or(key)) as _
42984275}
42994276
43004277/// Attributes that only make sense on container elements (@row, @column, @el).
@@ -4409,8 +4386,8 @@ fn validate_tree(
44094386 if let Node::Element(elem) = node {
44104387 for attr in &elem.attrs {
44114388 let base = strip_all_prefixes(&attr.key);
4412− if base == "width" && attr.value.as_deref() == Some("fill") {
4413− if !matches!(parent_kind, Some(ElementKind::Row)) {
4389+ if base == "width" && attr.value.as_deref() == Some("fill")
4390+ && !matches!(parent_kind, Some(ElementKind::Row)) {
44144391 diagnostics.push(Diagnostic {
44154392 line: elem.line_num,
44164393 column: None,
@@ -4420,9 +4397,8 @@ fn validate_tree(
44204397 source_line: None,
44214398 });
44224399 }
4423− }
4424− if base == "height" && attr.value.as_deref() == Some("fill") {
4425− if !matches!(parent_kind, Some(ElementKind::Column)) {
4400+ if base == "height" && attr.value.as_deref() == Some("fill")
4401+ && !matches!(parent_kind, Some(ElementKind::Column)) {
44264402 diagnostics.push(Diagnostic {
44274403 line: elem.line_num,
44284404 column: None,
@@ -4433,7 +4409,6 @@ fn validate_tree(
44334409 source_line: None,
44344410 });
44354411 }
4436− }
44374412
44384413 // Container-only attributes on non-container elements
44394414 if CONTAINER_ONLY_ATTRS.contains(&base) && !is_container(&elem.kind) {
@@ -4526,8 +4501,8 @@ fn validate_tree(
45264501 }
45274502 }
45284503 // Missing alt text on @image
4529− if matches!(elem.kind, ElementKind::Image) {
4530− if !elem.attrs.iter().any(|a| a.key == "alt") {
4504+ if matches!(elem.kind, ElementKind::Image)
4505+ && !elem.attrs.iter().any(|a| a.key == "alt") {
45314506 diagnostics.push(Diagnostic {
45324507 line: elem.line_num,
45334508 column: None,
@@ -4536,9 +4511,8 @@ fn validate_tree(
45364511 source_line: None,
45374512 });
45384513 }
4539− }
4540− if matches!(elem.kind, ElementKind::Input) {
4541− if !elem.attrs.iter().any(|a| a.key == "type") {
4514+ if matches!(elem.kind, ElementKind::Input)
4515+ && !elem.attrs.iter().any(|a| a.key == "type") {
45424516 diagnostics.push(Diagnostic {
45434517 line: elem.line_num,
45444518 column: None,
@@ -4547,7 +4521,6 @@ fn validate_tree(
45474521 source_line: None,
45484522 });
45494523 }
4550− }
45514524 if matches!(elem.kind, ElementKind::Link) {
45524525 // For @link, argument is the URL, not text content
45534526 let has_text = !elem.children.is_empty();
@@ -4570,8 +4543,8 @@ fn validate_tree(
45704543 let fg_color = elem.attrs.iter()
45714544 .find(|a| strip_all_prefixes(&a.key) == "color")
45724545 .and_then(|a| a.value.as_deref());
4573− if let (Some(bg), Some(fg)) = (bg_color, fg_color) {
4574− if let (Some(bg_rgb), Some(fg_rgb)) = (parse_hex_rgb(bg), parse_hex_rgb(fg)) {
4546+ if let (Some(bg), Some(fg)) = (bg_color, fg_color)
4547+ && let (Some(bg_rgb), Some(fg_rgb)) = (parse_hex_rgb(bg), parse_hex_rgb(fg)) {
45754548 let ratio = contrast_ratio(bg_rgb, fg_rgb);
45764549 if ratio < 4.5 {
45774550 diagnostics.push(Diagnostic {
@@ -4586,7 +4559,6 @@ fn validate_tree(
45864559 });
45874560 }
45884561 }
4589− }
45904562 }
45914563
45924564 // @form inputs should have associated @label
@@ -4610,8 +4582,8 @@ fn validate_tree(
46104582 }
46114583
46124584 // @iframe should have title attribute
4613− if matches!(elem.kind, ElementKind::Iframe) {
4614− if !elem.attrs.iter().any(|a| a.key == "title") {
4585+ if matches!(elem.kind, ElementKind::Iframe)
4586+ && !elem.attrs.iter().any(|a| a.key == "title") {
46154587 diagnostics.push(Diagnostic {
46164588 line: elem.line_num,
46174589 column: None,
@@ -4620,7 +4592,6 @@ fn validate_tree(
46204592 source_line: None,
46214593 });
46224594 }
4623− }
46244595
46254596 // @button should have accessible text
46264597 if matches!(elem.kind, ElementKind::Button) {
@@ -4655,10 +4626,10 @@ fn validate_tree(
46554626 }
46564627
46574628 // Tabindex > 0 is an anti-pattern
4658− if let Some(tabindex_attr) = elem.attrs.iter().find(|a| a.key == "tabindex") {
4659− if let Some(ref val) = tabindex_attr.value {
4660− if let Ok(n) = val.parse::<i32>() {
4661− if n > 0 {
4629+ if let Some(tabindex_attr) = elem.attrs.iter().find(|a| a.key == "tabindex")
4630+ && let Some(ref val) = tabindex_attr.value
4631+ && let Ok(n) = val.parse::<i32>()
4632+ && n > 0 {
46624633 diagnostics.push(Diagnostic {
46634634 line: elem.line_num,
46644635 column: None,
@@ -4667,9 +4638,6 @@ fn validate_tree(
46674638 source_line: None,
46684639 });
46694640 }
4670− }
4671− }
4672− }
46734641
46744642 validate_tree(&elem.children, Some(&elem.kind), diagnostics);
46754643 }
@@ -4875,11 +4843,10 @@ fn replace_extends_slots(
48754843
48764844fn apply_filter(value: &str, filter: &str) -> String {
48774845 if let Some(arg) = filter.strip_prefix("truncate:") {
4878− if let Ok(n) = arg.parse::<usize>() {
4879− if value.len() > n {
4846+ if let Ok(n) = arg.parse::<usize>()
4847+ && value.len() > n {
48804848 return format!("{}...", &value[..n]);
48814849 }
4882− }
48834850 return value.to_string();
48844851 }
48854852 if let Some(rest) = filter.strip_prefix("replace:") {
@@ -4896,37 +4863,34 @@ fn apply_filter(value: &str, filter: &str) -> String {
48964863 }
48974864 // Color functions: lighten:N, darken:N, alpha:N, mix:COLOR:N
48984865 if let Some(arg) = filter.strip_prefix("lighten:") {
4899− if let Ok(amount) = arg.parse::<f64>() {
4900− if let Some(rgb) = parse_hex_rgb(value) {
4866+ if let Ok(amount) = arg.parse::<f64>()
4867+ && let Some(rgb) = parse_hex_rgb(value) {
49014868 let (r, g, b) = lighten_color(rgb, amount / 100.0);
49024869 return format!("#{:02x}{:02x}{:02x}", r, g, b);
49034870 }
4904− }
49054871 return value.to_string();
49064872 }
49074873 if let Some(arg) = filter.strip_prefix("darken:") {
4908− if let Ok(amount) = arg.parse::<f64>() {
4909− if let Some(rgb) = parse_hex_rgb(value) {
4874+ if let Ok(amount) = arg.parse::<f64>()
4875+ && let Some(rgb) = parse_hex_rgb(value) {
49104876 let (r, g, b) = darken_color(rgb, amount / 100.0);
49114877 return format!("#{:02x}{:02x}{:02x}", r, g, b);
49124878 }
4913− }
49144879 return value.to_string();
49154880 }
49164881 if let Some(arg) = filter.strip_prefix("alpha:") {
4917− if let Ok(a) = arg.parse::<f64>() {
4918− if let Some((r, g, b)) = parse_hex_rgb(value) {
4882+ if let Ok(a) = arg.parse::<f64>()
4883+ && let Some((r, g, b)) = parse_hex_rgb(value) {
49194884 let a8 = (a.clamp(0.0, 1.0) * 255.0) as u8;
49204885 return format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a8);
49214886 }
4922− }
49234887 return value.to_string();
49244888 }
49254889 if let Some(arg) = filter.strip_prefix("mix:") {
49264890 // mix:COLOR:PERCENTAGE (e.g., mix:#ffffff:50)
49274891 let parts: Vec<&str> = arg.splitn(2, ':').collect();
4928− if parts.len() == 2 {
4929− if let (Some(c1), Some(c2), Ok(pct)) = (
4892+ if parts.len() == 2
4893+ && let (Some(c1), Some(c2), Ok(pct)) = (
49304894 parse_hex_rgb(value),
49314895 parse_hex_rgb(parts[0]),
49324896 parts[1].parse::<f64>(),
@@ -4934,7 +4898,6 @@ fn apply_filter(value: &str, filter: &str) -> String {
49344898 let (r, g, b) = mix_colors(c1, c2, pct / 100.0);
49354899 return format!("#{:02x}{:02x}{:02x}", r, g, b);
49364900 }
4937− }
49384901 return value.to_string();
49394902 }
49404903 match filter {
@@ -5309,7 +5272,7 @@ fn flatten_json(prefix: &str, value: &JsonValue, vars: &mut HashMap<String, Stri
53095272 vars.insert(prefix.to_string(), csv.join(","));
53105273 } else {
53115274 // Primitive array: comma-separated
5312− let csv: Vec<String> = items.iter().map(|v| json_value_to_string(v)).collect();
5275+ let csv: Vec<String> = items.iter().map(json_value_to_string).collect();
53135276 vars.insert(prefix.to_string(), csv.join(","));
53145277 }
53155278 // Also set indexed access: prefix.0, prefix.1, etc.
@@ -5390,14 +5353,13 @@ fn fetch_url_blocking(url: &str) -> Result<String, String> {
53905353 let body = &response_str[body_start + 4..];
53915354
53925355 // Check status code
5393− if let Some(first_line) = headers.lines().next() {
5394− if let Some(code_str) = first_line.split_whitespace().nth(1) {
5356+ if let Some(first_line) = headers.lines().next()
5357+ && let Some(code_str) = first_line.split_whitespace().nth(1) {
53955358 let code: u16 = code_str.parse().unwrap_or(0);
53965359 if code >= 400 {
53975360 return Err(format!("HTTP {}", code));
53985361 }
53995362 }
5400− }
54015363
54025364 // Handle chunked transfer encoding
54035365 if headers.to_lowercase().contains("transfer-encoding: chunked") {
@@ -5503,7 +5465,7 @@ fn markdown_to_html(lines: &[String]) -> String {
55035465 let trimmed = line.trim();
55045466
55055467 // Fenced code blocks
5506− if trimmed.starts_with("```") {
5468+ if let Some(after_fence) = trimmed.strip_prefix("```") {
55075469 if in_code_block {
55085470 html.push_str("<pre><code");
55095471 if !code_lang.is_empty() {
@@ -5517,7 +5479,7 @@ fn markdown_to_html(lines: &[String]) -> String {
55175479 in_code_block = false;
55185480 } else {
55195481 flush_para(&mut para_buf, &mut html);
5520− code_lang = trimmed[3..].trim().to_string();
5482+ code_lang = after_fence.trim().to_string();
55215483 in_code_block = true;
55225484 }
55235485 continue;
@@ -5565,15 +5527,14 @@ fn markdown_to_html(lines: &[String]) -> String {
55655527 }
55665528
55675529 // Ordered list items
5568− if let Some(dot_pos) = trimmed.find(". ") {
5569− if dot_pos <= 3 && trimmed[..dot_pos].chars().all(|c| c.is_ascii_digit()) {
5530+ if let Some(dot_pos) = trimmed.find(". ")
5531+ && dot_pos <= 3 && trimmed[..dot_pos].chars().all(|c| c.is_ascii_digit()) {
55705532 flush_para(&mut para_buf, &mut html);
55715533 if in_list { html.push_str("</ul>\n"); in_list = false; }
55725534 // We use <ol> but just emit <li> for simplicity
55735535 html.push_str(&format!("<li>{}</li>\n", md_inline(&trimmed[dot_pos + 2..])));
55745536 continue;
55755537 }
5576− }
55775538
55785539 // Horizontal rule
55795540 if trimmed == "---" || trimmed == "***" || trimmed == "___" {
@@ -5584,9 +5545,9 @@ fn markdown_to_html(lines: &[String]) -> String {
55845545 }
55855546
55865547 // Blockquote
5587− if trimmed.starts_with("> ") {
5548+ if let Some(quote_content) = trimmed.strip_prefix("> ") {
55885549 flush_para(&mut para_buf, &mut html);
5589− html.push_str(&format!("<blockquote><p>{}</p></blockquote>\n", md_inline(&trimmed[2..])));
5550+ html.push_str(&format!("<blockquote><p>{}</p></blockquote>\n", md_inline(quote_content)));
55905551 continue;
55915552 }
55925553
@@ -5635,8 +5596,8 @@ fn md_inline(text: &str) -> String {
56355596 }
56365597 }
56375598 // Inline code: `text`
5638− if chars[i] == '`' {
5639− if let Some(end) = chars[i+1..].iter().position(|&c| c == '`') {
5599+ if chars[i] == '`'
5600+ && let Some(end) = chars[i+1..].iter().position(|&c| c == '`') {
56405601 let inner: String = chars[i+1..i+1+end].iter().collect();
56415602 result.push_str("<code>");
56425603 result.push_str(&html_escape_md(&inner));
@@ -5644,22 +5605,19 @@ fn md_inline(text: &str) -> String {
56445605 i = i + 2 + end;
56455606 continue;
56465607 }
5647− }
56485608 // Links: [text](url)
5649− if chars[i] == '[' {
5650− if let Some(close_bracket) = chars[i+1..].iter().position(|&c| c == ']') {
5609+ if chars[i] == '['
5610+ && let Some(close_bracket) = chars[i+1..].iter().position(|&c| c == ']') {
56515611 let after = i + 1 + close_bracket + 1;
5652− if after < chars.len() && chars[after] == '(' {
5653− if let Some(close_paren) = chars[after+1..].iter().position(|&c| c == ')') {
5612+ if after < chars.len() && chars[after] == '('
5613+ && let Some(close_paren) = chars[after+1..].iter().position(|&c| c == ')') {
56545614 let text: String = chars[i+1..i+1+close_bracket].iter().collect();
56555615 let url: String = chars[after+1..after+1+close_paren].iter().collect();
56565616 result.push_str(&format!("<a href=\"{}\">{}</a>", html_escape_md(&url), md_inline(&text)));
56575617 i = after + 2 + close_paren;
56585618 continue;
56595619 }
5660− }
56615620 }
5662− }
56635621 result.push(chars[i]);
56645622 i += 1;
56655623 }
@@ -5679,12 +5637,7 @@ fn find_closing_double(chars: &[char], start: usize, marker: char) -> Option<usi
56795637}
56805638
56815639fn find_closing_single(chars: &[char], start: usize, marker: char) -> Option<usize> {
5682− for i in start..chars.len() {
5683− if chars[i] == marker {
5684− return Some(i);
5685− }
5686− }
5687− None
5640+ (start..chars.len()).find(|&i| chars[i] == marker)
56885641}
56895642
56905643/// Simple glob matching supporting `*` as wildcard for any characters and `?` for a single character.