Skip to main content

fallow_core/
errors.rs

1use std::path::PathBuf;
2
3/// Errors that can occur during analysis.
4#[derive(Debug)]
5pub enum FallowError {
6    /// Failed to read a source file.
7    FileReadError {
8        path: PathBuf,
9        source: std::io::Error,
10    },
11    /// Failed to parse a source file (syntax errors).
12    ParseError { path: PathBuf, errors: Vec<String> },
13    /// Failed to resolve an import.
14    ResolveError {
15        from_file: PathBuf,
16        specifier: String,
17    },
18    /// Configuration error.
19    ConfigError { message: String },
20}
21
22impl std::fmt::Display for FallowError {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            Self::FileReadError { path, source } => {
26                write!(f, "Failed to read {}: {source}", path.display())
27            }
28            Self::ParseError { path, errors } => {
29                write!(
30                    f,
31                    "Parse errors in {} ({} errors)",
32                    path.display(),
33                    errors.len()
34                )
35            }
36            Self::ResolveError {
37                from_file,
38                specifier,
39            } => {
40                write!(
41                    f,
42                    "Cannot resolve '{}' from {}",
43                    specifier,
44                    from_file.display()
45                )
46            }
47            Self::ConfigError { message } => {
48                write!(f, "Configuration error: {message}")
49            }
50        }
51    }
52}
53
54impl std::error::Error for FallowError {}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn fallow_error_display_file_read() {
62        let err = FallowError::FileReadError {
63            path: PathBuf::from("test.ts"),
64            source: std::io::Error::new(std::io::ErrorKind::NotFound, "not found"),
65        };
66        let msg = format!("{err}");
67        assert!(msg.contains("test.ts"));
68        assert!(msg.contains("not found"));
69    }
70
71    #[test]
72    fn fallow_error_display_parse() {
73        let err = FallowError::ParseError {
74            path: PathBuf::from("bad.ts"),
75            errors: vec![
76                "unexpected token".to_string(),
77                "missing semicolon".to_string(),
78            ],
79        };
80        let msg = format!("{err}");
81        assert!(msg.contains("bad.ts"));
82        assert!(msg.contains("2 errors"));
83    }
84
85    #[test]
86    fn fallow_error_display_resolve() {
87        let err = FallowError::ResolveError {
88            from_file: PathBuf::from("src/index.ts"),
89            specifier: "./missing".to_string(),
90        };
91        let msg = format!("{err}");
92        assert!(msg.contains("./missing"));
93        assert!(msg.contains("src/index.ts"));
94    }
95
96    #[test]
97    fn fallow_error_display_config() {
98        let err = FallowError::ConfigError {
99            message: "invalid TOML".to_string(),
100        };
101        let msg = format!("{err}");
102        assert!(msg.contains("invalid TOML"));
103    }
104}