Skip to main content

flake_edit/walk/
error.rs

1use thiserror::Error;
2
3/// Errors that can occur during AST walking and manipulation.
4#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum WalkerError {
7    /// rnix did not produce a parseable root for this input.
8    #[error("flake.nix is not a parseable Nix file")]
9    NotARoot,
10
11    /// The top level of `flake.nix` contained something other than an
12    /// `attr = value;` pair. `snippet` is a short excerpt of the offending
13    /// node and `offset` is the byte offset where it starts.
14    #[error("unexpected non-attribute at top level of flake.nix at byte {offset}: {snippet}")]
15    UnexpectedTopLevel { snippet: String, offset: u32 },
16}
17
18impl WalkerError {
19    pub(crate) fn unexpected_top_level(text: &str, offset: u32) -> Self {
20        const MAX_SNIPPET: usize = 60;
21
22        let single_line: String = text
23            .replace(['\n', '\r'], " ")
24            .split_whitespace()
25            .collect::<Vec<_>>()
26            .join(" ");
27        let snippet = if single_line.chars().count() > MAX_SNIPPET {
28            let truncated: String = single_line.chars().take(MAX_SNIPPET).collect();
29            format!("{truncated}...")
30        } else {
31            single_line
32        };
33        Self::UnexpectedTopLevel { snippet, offset }
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::WalkerError;
40
41    #[test]
42    fn unexpected_top_level_truncates_long_snippets() {
43        let long = "x".repeat(200);
44        let err = WalkerError::unexpected_top_level(&long, 7);
45        let s = err.to_string();
46        assert!(s.contains("..."), "long snippet should be truncated: {s}");
47        assert!(s.contains("byte 7"), "byte offset should survive: {s}");
48    }
49
50    #[test]
51    fn unexpected_top_level_collapses_newlines() {
52        let err = WalkerError::unexpected_top_level("let\n  x = 1;\nin x", 3);
53        let s = err.to_string();
54        assert!(!s.contains('\n'), "newlines should be collapsed: {s:?}");
55        assert!(s.ends_with("let x = 1; in x"));
56    }
57}