Skip to main content

orrery_parser/error/
parse_error.rs

1//! The ParseError type for wrapping parsing diagnostics.
2//!
3//! [`ParseError`] wraps one or more [`Diagnostic`]s that occurred during
4//! the parsing lifecycle along with the [`SourceMap`] that maps virtual
5//! spans to file locations.
6
7use std::fmt;
8
9use crate::{error::Diagnostic, source_map::SourceMap};
10
11/// A type alias for `Result<T, Diagnostic>`.
12pub type Result<T> = std::result::Result<T, Diagnostic>;
13
14/// Error type for the parsing lifecycle.
15///
16/// Wraps one or more diagnostics together with the [`SourceMap`].
17#[derive(Debug, thiserror::Error)]
18pub struct ParseError<'a> {
19    diagnostics: Vec<Diagnostic>,
20    source_map: SourceMap<'a>,
21}
22
23impl fmt::Display for ParseError<'_> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        if let Some(first) = self.diagnostics.first() {
26            write!(f, "{}", first)?;
27            if self.diagnostics.len() > 1 {
28                write!(f, " (+{} more)", self.diagnostics.len() - 1)?;
29            }
30        }
31        Ok(())
32    }
33}
34
35impl<'a> ParseError<'a> {
36    /// Create a new parse error from diagnostics and a source map.
37    pub fn new(diagnostics: Vec<Diagnostic>, source_map: SourceMap<'a>) -> Self {
38        Self {
39            diagnostics,
40            source_map,
41        }
42    }
43
44    /// Create a parse error from a single diagnostic and a source map.
45    pub fn from_diagnostic(diagnostic: Diagnostic, source_map: SourceMap<'a>) -> Self {
46        Self {
47            diagnostics: vec![diagnostic],
48            source_map,
49        }
50    }
51
52    /// Get the source map associated with this error.
53    pub fn source_map(&self) -> &SourceMap<'a> {
54        &self.source_map
55    }
56
57    /// Get all diagnostics in this error.
58    pub fn diagnostics(&self) -> &[Diagnostic] {
59        &self.diagnostics
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::error::ErrorCode;
67
68    #[test]
69    fn test_parse_error_new() {
70        let diag = Diagnostic::error("test error").with_code(ErrorCode::E300);
71        let err = ParseError::new(vec![diag], SourceMap::new());
72
73        assert_eq!(err.diagnostics().len(), 1);
74        assert_eq!(err.diagnostics()[0].message(), "test error");
75    }
76
77    #[test]
78    fn test_parse_error_new_multi_diagnostics() {
79        let diags = vec![Diagnostic::error("error 1"), Diagnostic::error("error 2")];
80        let err = ParseError::new(diags, SourceMap::new());
81
82        assert_eq!(err.diagnostics().len(), 2);
83    }
84
85    #[test]
86    fn test_parse_error_from_diagnostic() {
87        let diag = Diagnostic::error("test error").with_code(ErrorCode::E300);
88        let err = ParseError::from_diagnostic(diag, SourceMap::new());
89
90        assert_eq!(err.diagnostics().len(), 1);
91        assert_eq!(err.diagnostics()[0].message(), "test error");
92    }
93
94    #[test]
95    fn test_parse_error_source_map_accessor() {
96        let mut sm = SourceMap::new();
97        sm.add_file("test.orr", "hello", None);
98        let err = ParseError::new(vec![Diagnostic::error("err")], sm);
99
100        assert_eq!(err.source_map().file_count(), 1);
101    }
102
103    #[test]
104    fn test_parse_error_display_single() {
105        let diag = Diagnostic::error("undefined type");
106        let err = ParseError::from_diagnostic(diag, SourceMap::new());
107
108        assert_eq!(err.to_string(), "error: undefined type");
109    }
110
111    #[test]
112    fn test_parse_error_display_multiple() {
113        let diags = vec![
114            Diagnostic::error("first error"),
115            Diagnostic::error("second error"),
116            Diagnostic::error("third error"),
117        ];
118        let err = ParseError::new(diags, SourceMap::new());
119
120        assert_eq!(err.to_string(), "error: first error (+2 more)");
121    }
122}