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