1use hemtt_error::{make_source, thiserror, PrettyError, Source};
2use hemtt_tokens::{Symbol, Token};
3
4#[derive(thiserror::Error, Debug)]
5pub enum Error {
7 #[error("Expected `{expected:?}`, found `{token:?}`")]
8 UnexpectedToken {
10 token: Box<Token>,
12 expected: Vec<Symbol>,
14 },
15 #[error("Unexpected EOF at `{token:?}`")]
16 UnexpectedEOF {
18 token: Box<Token>,
20 },
21 #[error("Expected `{{ident}}`, found `{token:?}`")]
22 ExpectedIdent {
24 token: Box<Token>,
26 },
27 #[error("Expected `{{number}}`, found `{token:?}`")]
28 ExpectedNumber {
30 token: Box<Token>,
32 },
33
34 #[error("IO Error: {0}")]
35 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}