hemtt_config/
error.rs

1use hemtt_error::{make_source, thiserror, PrettyError, Source};
2use hemtt_tokens::{Symbol, Token};
3
4#[derive(thiserror::Error, Debug)]
5/// Error type for the config parser
6pub enum Error {
7    #[error("Expected `{expected:?}`, found `{token:?}`")]
8    /// Expected a different token in the current context
9    UnexpectedToken {
10        /// The token that was found
11        token: Box<Token>,
12        /// The token that was expected
13        expected: Vec<Symbol>,
14    },
15    #[error("Unexpected EOF at `{token:?}`")]
16    /// Unexpected end of file
17    UnexpectedEOF {
18        /// The token that was found
19        token: Box<Token>,
20    },
21    #[error("Expected `{{ident}}`, found `{token:?}`")]
22    /// Expected an identifier in the current context
23    ExpectedIdent {
24        /// The token that was found
25        token: Box<Token>,
26    },
27    #[error("Expected `{{number}}`, found `{token:?}`")]
28    /// Expected a number in the current context
29    ExpectedNumber {
30        /// The token that was found
31        token: Box<Token>,
32    },
33
34    #[error("IO Error: {0}")]
35    /// [std::io::Error]
36    Io(Box<std::io::Error>),
37}
38
39impl From<std::io::Error> for Error {
40    fn from(e: std::io::Error) -> Self {
41        Self::Io(Box::new(e))
42    }
43}
44
45impl PrettyError for Error {
46    fn brief(&self) -> String {
47        match self {
48            Self::UnexpectedToken { token, expected } => {
49                format!(
50                    "Expected `{expected:?}`, found `{symbol:?}`,",
51                    symbol = token.symbol(),
52                    expected = expected
53                )
54            }
55            Self::UnexpectedEOF { token } => {
56                format!("Unexpected EOF near `{token:?}`,")
57            }
58            Self::ExpectedIdent { token } => {
59                format!(
60                    "Expected `{{ident}}`, found `{symbol:?}`,",
61                    symbol = token.symbol()
62                )
63            }
64            Self::ExpectedNumber { token } => {
65                format!(
66                    "Expected `{{number}}`, found `{symbol:?}`,",
67                    symbol = token.symbol()
68                )
69            }
70            Self::Io(e) => {
71                format!("IO Error: {e}")
72            }
73        }
74    }
75
76    fn details(&self) -> Option<String> {
77        None
78    }
79
80    fn help(&self) -> Option<String> {
81        match self {
82            Self::UnexpectedToken { token, expected } => {
83                if expected == &[Symbol::LeftBrace, Symbol::DoubleQuote, Symbol::Digit(0)] {
84                    if let Symbol::Word(_) = token.symbol() {
85                        return Some("Did you forget to place quotes around a string? Or perhaps you forgot to define / import a value.".to_string());
86                    }
87                    if token.symbol() == &Symbol::Escape {
88                        return Some("Did you forget to place quotes around a string? Or perhaps you forgot to use Q infront of a path macro.".to_string());
89                    }
90                } else if expected == &[Symbol::Semicolon] {
91                    return Some("Did you forget to place a semicolon at the end of a line? Or perhaps you are missing quotes around a string?".to_string());
92                }
93            }
94            Self::ExpectedIdent { token: _ } => {
95                return Some("Is something quoted that shouldn't be?".to_string());
96            }
97            _ => (),
98        }
99        None
100    }
101
102    fn source(&self) -> Option<Box<Source>> {
103        match self {
104            Self::UnexpectedToken { token, expected } => {
105                make_source(token, format!("expected one of: {expected:?}"))
106                    .ok()
107                    .map(Box::new)
108            }
109            Self::ExpectedIdent { token } => make_source(token, "expected: <ident>".to_string())
110                .ok()
111                .map(Box::new),
112            Self::ExpectedNumber { token } => make_source(token, "expected: <number>".to_string())
113                .ok()
114                .map(Box::new),
115            _ => None,
116        }
117    }
118
119    fn trace(&self) -> Vec<Source> {
120        let mut parent = match self {
121            Self::ExpectedIdent { token }
122            | Self::UnexpectedToken { token, expected: _ }
123            | Self::ExpectedNumber { token }
124            | Self::UnexpectedEOF { token } => token.parent(),
125            Self::Io(_) => &None,
126        };
127        let mut trace = Vec::new();
128        while let Some(p) = parent {
129            parent = p.parent();
130            let source = make_source(p, String::new());
131            if let Ok(source) = source {
132                trace.push(source);
133            }
134        }
135        trace.reverse();
136        trace
137    }
138}