Skip to main content

meta_ast/
error.rs

1use std::path::PathBuf;
2
3use crate::model::SourceRange;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
6#[non_exhaustive]
7pub enum Severity {
8    Warning,
9    Error,
10}
11
12#[derive(Debug, Clone, serde::Serialize)]
13pub struct Diagnostic {
14    pub path: PathBuf,
15    pub severity: Severity,
16    pub message: String,
17    pub source_range: Option<SourceRange>,
18}
19
20#[derive(Debug, thiserror::Error)]
21pub enum Error {
22    #[error("IO: {0}")]
23    Io(#[from] std::io::Error),
24
25    #[error("parse error in {path}: {message}")]
26    Parse { path: PathBuf, message: String },
27
28    #[error("query error ({language}): {message}")]
29    Query {
30        language: crate::language::LangId,
31        message: String,
32    },
33
34    #[error("config: {0}")]
35    Config(String),
36
37    #[error("graph error: {0}")]
38    Graph(String),
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::language::LangId;
45    use std::path::PathBuf;
46
47    #[test]
48    fn io_error_from_std_io() {
49        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
50        let err: Error = io_err.into();
51        assert!(matches!(err, Error::Io(_)));
52        assert!(err.to_string().contains("file not found"));
53    }
54
55    #[test]
56    fn parse_error_display_format() {
57        let err = Error::Parse {
58            path: PathBuf::from("foo.bar"),
59            message: "bad syntax".into(),
60        };
61        let displayed = err.to_string();
62        assert!(displayed.contains("foo.bar"), "{displayed}");
63        assert!(displayed.contains("bad syntax"), "{displayed}");
64        assert!(displayed.starts_with("parse error in"), "{displayed}");
65    }
66
67    #[test]
68    fn query_error_display_format() {
69        let err = Error::Query {
70            language: LangId::Rust,
71            message: "no match".into(),
72        };
73        let displayed = err.to_string();
74        assert!(displayed.contains("rust"), "{displayed}");
75        assert!(displayed.contains("no match"), "{displayed}");
76        assert!(displayed.starts_with("query error"), "{displayed}");
77    }
78
79    #[test]
80    fn config_error_display() {
81        let err = Error::Config("bad setting".into());
82        let displayed = err.to_string();
83        assert!(displayed.starts_with("config:"), "{displayed}");
84        assert!(displayed.contains("bad setting"), "{displayed}");
85    }
86
87    #[test]
88    fn diagnostic_construction() {
89        let sr = SourceRange {
90            byte_start: 0,
91            byte_end: 10,
92            start: crate::model::LineColumn { line: 1, column: 0 },
93            end: crate::model::LineColumn {
94                line: 1,
95                column: 10,
96            },
97        };
98        let d = Diagnostic {
99            path: PathBuf::from("test.rs"),
100            severity: Severity::Error,
101            message: "undefined variable".into(),
102            source_range: Some(sr.clone()),
103        };
104        assert_eq!(d.path, PathBuf::from("test.rs"));
105        assert_eq!(d.severity, Severity::Error);
106        assert_eq!(d.message, "undefined variable");
107        assert!(d.source_range.is_some());
108        let range = d.source_range.unwrap();
109        assert_eq!(range.byte_start, 0);
110        assert_eq!(range.byte_end, 10);
111    }
112
113    #[test]
114    fn severity_variants() {
115        assert_ne!(Severity::Warning, Severity::Error);
116        let v = [Severity::Warning, Severity::Error];
117        assert_eq!(v.len(), 2);
118    }
119
120    #[test]
121    fn graph_error_display_format() {
122        let err = Error::Graph("cycle detected".into());
123        let displayed = err.to_string();
124        assert!(displayed.starts_with("graph error:"), "{displayed}");
125        assert!(displayed.contains("cycle detected"), "{displayed}");
126    }
127
128    #[test]
129    fn graph_error_matches() {
130        let err = Error::Graph("test".into());
131        assert!(matches!(err, Error::Graph(_)));
132    }
133}