{"record":{"id":"365a2c9a55fd6664","repo":"elkowar/eww","slug":"something-went-wrong-unindenting-the-string","errorCode":null,"errorMessage":"Something went wrong unindenting the string","messagePattern":"Something went wrong unindenting the string","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/eww/src/util.rs","lineNumber":128,"sourceCode":"        .replace_all(&input, |var_name: &regex::Captures| std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default())\n        .into_owned()\n}\n\npub fn unindent(text: &str) -> String {\n    // take all the lines of our text and skip over the first empty ones\n    let lines = text.lines().skip_while(|x| x.is_empty());\n    // find the smallest indentation\n    let min = lines\n        .clone()\n        .fold(None, |min, line| {\n            let min = min.unwrap_or(usize::MAX);\n            Some(min.min(line.chars().take(min).take_while(|&c| c == ' ').count()))\n        })\n        .unwrap_or(0);\n\n    let mut result = String::new();\n    for i in lines {\n        writeln!(result, \"{}\", &i[min..]).expect(\"Something went wrong unindenting the string\");\n    }\n    result.pop();\n    result\n}\n\n#[cfg(test)]\nmod test {\n    use super::{replace_env_var_references, unindent};\n\n    #[test]\n    fn test_replace_env_var_references() {\n        let scss = \"$test: ${USER};\";\n\n        assert_eq!(\n            replace_env_var_references(String::from(scss)),\n            format!(\"$test: {};\", std::env::var(\"USER\").unwrap_or_default())\n        )\n    }","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/elkowar/eww/blob/48f5aa8b379adf29da0b0bb9ca04164f65d8bdaa/crates/eww/src/util.rs#L110-L146","documentation":"unindent strips a common leading-space indent from each line by slicing &i[min..]. The writeln!(...) expect fires if slicing/formatting panics — in practice when min exceeds a line's char length, so the slice start is out of bounds. It is a logic guard in the string-unindenting helper.","triggerScenarios":"Calling unindent on text where the computed common indent `min` (computed over chars) is larger than the byte/char length of some line — e.g. lines mixing indentation such that `min` derived from char counts misaligns with byte indexing used by `[min..]`, or a line shorter than min.","commonSituations":"Developers using eww's util::unindent on hand-built multi-line strings with inconsistent leading whitespace, tabs mixed with spaces, or trailing empty lines with fewer characters than the indent — mostly hit in tests or when generating inline templates/docs.","solutions":["Normalize the input: ensure every line is prefixed with the same whitespace before calling unindent (e.g. use the indoc crate instead).","Trim lines defensively: skip/clip lines shorter than `min` before slicing.","Replace the hand-rolled unindent with `indoc::indoc!` or `unindent` crate, which handle edge cases.","Compute `min` as a byte index consistently and clamp it: `let start = min.min(i.len());` before slicing."],"exampleFix":"// before\nfor i in lines {\n    writeln!(result, \"{}\", &i[min..]).expect(\"Something went wrong unindenting the string\");\n}\n// after\nfor i in lines {\n    let start = min.min(i.len());\n    writeln!(result, \"{}\", &i[start..]);\n}","handlingStrategy":"validation","validationCode":"// guard before slicing\nif line.len() < min { continue; } // or clamp: let start = min.min(line.len());","typeGuard":null,"tryCatchPattern":"// slicing panic; avoid by clamping the index instead of catching\nlet start = min.min(i.len());\nwriteln!(result, \"{}\", &i[start..]).ok();","preventionTips":["Normalize whitespace (expand tabs to spaces) before unindenting","Prefer the indoc or unindent crates over hand-rolled slicing","Use char_indices/char-aware offsets consistently","Add tests with empty lines and mixed indentation"],"tags":["string-processing","panic","utility"],"backgroundTag":"internal-invariant-violation","analyzedSha":"48f5aa8b379adf29da0b0bb9ca04164f65d8bdaa","analyzedAt":"2026-09-08T03:13:26.897Z","contentChangedAt":"2026-09-08T03:13:26.897Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}