1use crate::span::Span;
2use std::error::Error;
3use std::fmt;
4
5pub type Result<T> = std::result::Result<T, ZuzuRustError>;
6
7#[derive(Debug)]
8pub enum ZuzuRustError {
9 Lex {
10 message: String,
11 source_file: Option<String>,
12 line: usize,
13 column: usize,
14 },
15 Parse {
16 message: String,
17 source_file: Option<String>,
18 line: usize,
19 column: usize,
20 },
21 IncompleteParse {
22 message: String,
23 source_file: Option<String>,
24 line: usize,
25 column: usize,
26 },
27 Semantic {
28 message: String,
29 source_file: Option<String>,
30 line: usize,
31 column: usize,
32 },
33 Thrown {
34 value: String,
35 token: Option<String>,
36 },
37 Runtime {
38 message: String,
39 },
40 Cli {
41 message: String,
42 },
43 Io(std::io::Error),
44}
45
46impl ZuzuRustError {
47 pub fn parse(message: impl Into<String>, span: Span) -> Self {
48 Self::Parse {
49 message: message.into(),
50 source_file: None,
51 line: span.line,
52 column: span.column,
53 }
54 }
55
56 pub fn incomplete_parse(message: impl Into<String>, span: Span) -> Self {
57 Self::IncompleteParse {
58 message: message.into(),
59 source_file: None,
60 line: span.line,
61 column: span.column,
62 }
63 }
64
65 pub fn lex(message: impl Into<String>, line: usize, column: usize) -> Self {
66 Self::Lex {
67 message: message.into(),
68 source_file: None,
69 line,
70 column,
71 }
72 }
73
74 pub fn semantic(message: impl Into<String>, line: usize) -> Self {
75 Self::Semantic {
76 message: message.into(),
77 source_file: None,
78 line,
79 column: 1,
80 }
81 }
82
83 pub fn cli(message: impl Into<String>) -> Self {
84 Self::Cli {
85 message: message.into(),
86 }
87 }
88
89 pub fn runtime(message: impl Into<String>) -> Self {
90 Self::Runtime {
91 message: message.into(),
92 }
93 }
94
95 pub fn thrown(value: impl Into<String>) -> Self {
96 Self::Thrown {
97 value: value.into(),
98 token: None,
99 }
100 }
101
102 pub fn thrown_with_token(value: impl Into<String>, token: impl Into<String>) -> Self {
103 Self::Thrown {
104 value: value.into(),
105 token: Some(token.into()),
106 }
107 }
108
109 pub fn with_source_file(mut self, source_file: Option<&str>) -> Self {
110 let Some(source_file) = source_file else {
111 return self;
112 };
113 let source_file = source_file.to_owned();
114 match &mut self {
115 ZuzuRustError::Lex {
116 source_file: current,
117 ..
118 }
119 | ZuzuRustError::Parse {
120 source_file: current,
121 ..
122 }
123 | ZuzuRustError::IncompleteParse {
124 source_file: current,
125 ..
126 }
127 | ZuzuRustError::Semantic {
128 source_file: current,
129 ..
130 } => {
131 current.get_or_insert(source_file);
132 }
133 _ => {}
134 }
135 self
136 }
137
138 pub fn is_iterator_exhausted(&self) -> bool {
139 matches!(self, ZuzuRustError::Runtime { message } if message == "iterator exhausted")
140 }
141
142 pub fn thrown_value(&self) -> Option<&str> {
143 match self {
144 ZuzuRustError::Thrown { value, .. } => Some(value),
145 _ => None,
146 }
147 }
148}
149
150impl fmt::Display for ZuzuRustError {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 ZuzuRustError::Lex {
154 message,
155 source_file,
156 line,
157 column,
158 } => write_diagnostic(
159 f,
160 "lex error",
161 source_file.as_deref(),
162 *line,
163 *column,
164 message,
165 ),
166 ZuzuRustError::Parse {
167 message,
168 source_file,
169 line,
170 column,
171 } => write_diagnostic(
172 f,
173 "parse error",
174 source_file.as_deref(),
175 *line,
176 *column,
177 message,
178 ),
179 ZuzuRustError::IncompleteParse {
180 message,
181 source_file,
182 line,
183 column,
184 } => write_diagnostic(
185 f,
186 "incomplete parse error",
187 source_file.as_deref(),
188 *line,
189 *column,
190 message,
191 ),
192 ZuzuRustError::Semantic {
193 message,
194 source_file,
195 line,
196 column,
197 } => write_diagnostic(
198 f,
199 "semantic error",
200 source_file.as_deref(),
201 *line,
202 *column,
203 message,
204 ),
205 ZuzuRustError::Thrown { value, .. } => {
206 write!(f, "uncaught exception: {value}")
207 }
208 ZuzuRustError::Runtime { message } => write!(f, "runtime error: {message}"),
209 ZuzuRustError::Cli { message } => write!(f, "{message}"),
210 ZuzuRustError::Io(err) => write!(f, "io error: {err}"),
211 }
212 }
213}
214
215fn write_diagnostic(
216 f: &mut fmt::Formatter<'_>,
217 kind: &str,
218 source_file: Option<&str>,
219 line: usize,
220 column: usize,
221 message: &str,
222) -> fmt::Result {
223 match source_file {
224 Some(source_file) => write!(f, "{kind} at {source_file}:{line}:{column}: {message}"),
225 None => write!(f, "{kind} at {line}:{column}: {message}"),
226 }
227}
228
229impl Error for ZuzuRustError {}
230
231impl From<std::io::Error> for ZuzuRustError {
232 fn from(value: std::io::Error) -> Self {
233 Self::Io(value)
234 }
235}