1#![allow(clippy::question_mark)]
2
3#[cfg(test)]
4#[path = "./error_tests.rs"]
5mod tests;
6
7use crate::Span;
8use std::fmt::{self, Debug, Display};
9
10pub struct Error {
14 pub kind: ErrorKind,
16 pub span: Span,
21}
22
23impl std::error::Error for Error {}
24
25impl Debug for Error {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.debug_struct("Error")
28 .field("kind", &self.kind)
29 .field("span", &self.span)
30 .finish()
31 }
32}
33
34impl From<(ErrorKind, Span)> for Error {
35 fn from((kind, span): (ErrorKind, Span)) -> Self {
36 Self { kind, span }
37 }
38}
39
40pub enum ErrorKind {
42 UnexpectedEof,
44
45 FileTooLarge,
47
48 InvalidCharInString(char),
50
51 InvalidEscape(char),
53
54 InvalidHexEscape(char),
56
57 InvalidEscapeValue(u32),
61
62 Unexpected(char),
65
66 UnterminatedString,
69
70 InvalidNumber,
72
73 OutOfRange(&'static str),
76
77 Wanted {
79 expected: &'static str,
81 found: &'static str,
83 },
84
85 DuplicateTable {
87 name: String,
89 first: Span,
91 },
92
93 DuplicateKey {
95 key: String,
97 first: Span,
99 },
100
101 RedefineAsArray,
103
104 MultilineStringKey,
106
107 Custom(std::borrow::Cow<'static, str>),
110
111 DottedKeyInvalidType {
113 first: Span,
115 },
116
117 UnexpectedKeys {
121 keys: Vec<(String, Span)>,
123 },
124
125 UnquotedString,
127
128 MissingField(&'static str),
130
131 Deprecated {
133 old: &'static str,
135 new: &'static str,
137 },
138
139 UnexpectedValue {
141 expected: &'static [&'static str],
143 value: Option<String>,
145 },
146}
147
148impl Display for ErrorKind {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 let text = match self {
151 Self::UnexpectedEof => "unexpected-eof",
152 Self::FileTooLarge => "file-too-large",
153 Self::Custom(..) => "custom",
154 Self::DottedKeyInvalidType { .. } => "dotted-key-invalid-type",
155 Self::DuplicateKey { .. } => "duplicate-key",
156 Self::DuplicateTable { .. } => "duplicate-table",
157 Self::UnexpectedKeys { .. } => "unexpected-keys",
158 Self::UnquotedString => "unquoted-string",
159 Self::MultilineStringKey => "multiline-string-key",
160 Self::RedefineAsArray => "redefine-as-array",
161 Self::InvalidCharInString(..) => "invalid-char-in-string",
162 Self::InvalidEscape(..) => "invalid-escape",
163 Self::InvalidEscapeValue(..) => "invalid-escape-value",
164 Self::InvalidHexEscape(..) => "invalid-hex-escape",
165 Self::Unexpected(..) => "unexpected",
166 Self::UnterminatedString => "unterminated-string",
167 Self::InvalidNumber => "invalid-number",
168 Self::OutOfRange(_) => "out-of-range",
169 Self::Wanted { .. } => "wanted",
170 Self::MissingField(..) => "missing-field",
171 Self::Deprecated { .. } => "deprecated",
172 Self::UnexpectedValue { .. } => "unexpected-value",
173 };
174 f.write_str(text)
175 }
176}
177
178impl Debug for ErrorKind {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 Display::fmt(self, f)
181 }
182}
183
184struct Escape(char);
185
186impl fmt::Display for Escape {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 use std::fmt::Write as _;
189
190 if self.0.is_whitespace() {
191 for esc in self.0.escape_default() {
192 f.write_char(esc)?;
193 }
194 Ok(())
195 } else {
196 f.write_char(self.0)
197 }
198 }
199}
200
201macro_rules! rtry {
202 ($($tt:tt)*) => {
203 if let Err(err) = $($tt)* {
204 return Err(err);
205 }
206 };
207}
208impl Display for Error {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 match &self.kind {
211 ErrorKind::UnexpectedEof => f.write_str("unexpected eof encountered"),
212 ErrorKind::FileTooLarge => f.write_str("file is too large (maximum 4GiB)"),
213 ErrorKind::InvalidCharInString(c) => {
214 rtry!(f.write_str("invalid character in string: `"));
215 rtry!(std::fmt::Display::fmt(c, f));
216 f.write_str("`")
217 }
218 ErrorKind::InvalidEscape(c) => {
219 rtry!(f.write_str("invalid escape character in string: `"));
220 rtry!(Escape(*c).fmt(f));
221 f.write_str("`")
222 }
223 ErrorKind::InvalidHexEscape(c) => {
224 rtry!(f.write_str("invalid hex escape character in string: `"));
225 rtry!(std::fmt::Display::fmt(c, f));
226 f.write_str("`")
227 }
228 ErrorKind::InvalidEscapeValue(c) => {
229 rtry!(f.write_str("invalid escape value: `"));
230 rtry!(std::fmt::Display::fmt(c, f));
231 f.write_str("`")
232 }
233 ErrorKind::Unexpected(c) => {
234 rtry!(f.write_str("unexpected character found: `"));
235 rtry!(std::fmt::Display::fmt(c, f));
236 f.write_str("`")
237 }
238 ErrorKind::UnterminatedString => f.write_str("unterminated string"),
239 ErrorKind::Wanted { expected, found } => {
240 rtry!(f.write_str("expected "));
241 rtry!(f.write_str(expected));
242 rtry!(f.write_str(", found "));
243 f.write_str(found)
244 }
245 ErrorKind::InvalidNumber => f.write_str("invalid number"),
246 ErrorKind::OutOfRange(kind) => {
247 rtry!(f.write_str("out of range of '"));
248 rtry!(f.write_str(kind));
249 f.write_str("'")
250 }
251 ErrorKind::DuplicateTable { name, .. } => {
252 rtry!(f.write_str("redefinition of table `"));
253 rtry!(f.write_str(name));
254 f.write_str("`")
255 }
256 ErrorKind::DuplicateKey { key, .. } => {
257 rtry!(f.write_str("duplicate key: `"));
258 rtry!(f.write_str(key));
259 f.write_str("`")
260 }
261 ErrorKind::RedefineAsArray => f.write_str("table redefined as array"),
262 ErrorKind::MultilineStringKey => {
263 f.write_str("multiline strings are not allowed for key")
264 }
265 ErrorKind::Custom(message) => f.write_str(message),
266 ErrorKind::DottedKeyInvalidType { .. } => {
267 f.write_str("dotted key attempted to extend non-table type")
268 }
269 ErrorKind::UnexpectedKeys { keys } => {
270 rtry!(f.write_str("unexpected keys in table: ["));
271 let mut first = true;
272 for (key, _) in keys {
273 if !first {
274 rtry!(f.write_str(", "));
275 }
276 first = false;
277 rtry!(f.write_str("\""));
278 rtry!(f.write_str(key));
279 rtry!(f.write_str("\""));
280 }
281 f.write_str("]")
282 }
283 ErrorKind::UnquotedString => {
284 f.write_str("invalid TOML value, did you mean to use a quoted string?")
285 }
286 ErrorKind::MissingField(field) => {
287 rtry!(f.write_str("missing field '"));
288 rtry!(f.write_str(field));
289 f.write_str("' in table")
290 }
291 ErrorKind::Deprecated { old, new } => {
292 rtry!(f.write_str("field '"));
293 rtry!(f.write_str(old));
294 rtry!(f.write_str("' is deprecated, '"));
295 rtry!(f.write_str(new));
296 f.write_str("' has replaced it")
297 }
298 ErrorKind::UnexpectedValue { expected, .. } => {
299 rtry!(f.write_str("expected '["));
300 let mut first = true;
301 for val in *expected {
302 if !first {
303 rtry!(f.write_str(", "));
304 }
305 first = false;
306 rtry!(f.write_str(val));
307 }
308 f.write_str("]'")
309 }
310 }
311 }
312}
313
314#[cfg(feature = "reporting")]
315impl Error {
316 pub fn to_diagnostic<FileId: Copy + PartialEq>(
318 &self,
319 fid: FileId,
320 ) -> codespan_reporting::diagnostic::Diagnostic<FileId> {
321 let diag =
322 codespan_reporting::diagnostic::Diagnostic::error().with_code(self.kind.to_string());
323
324 use codespan_reporting::diagnostic::Label;
325
326 match &self.kind {
327 ErrorKind::DuplicateKey { first, .. } => diag.with_labels(vec![
328 Label::secondary(fid, *first).with_message("first key instance"),
329 Label::primary(fid, self.span).with_message("duplicate key"),
330 ]),
331 ErrorKind::Unexpected(c) => diag.with_labels(vec![
332 Label::primary(fid, self.span)
333 .with_message(format!("unexpected character '{}'", Escape(*c))),
334 ]),
335 ErrorKind::InvalidCharInString(c) => diag.with_labels(vec![
336 Label::primary(fid, self.span)
337 .with_message(format!("invalid character '{}' in string", Escape(*c))),
338 ]),
339 ErrorKind::InvalidEscape(c) => {
340 diag.with_labels(vec![Label::primary(fid, self.span).with_message(format!(
341 "invalid escape character '{}' in string",
342 Escape(*c)
343 ))])
344 }
345 ErrorKind::InvalidEscapeValue(_) => diag.with_labels(vec![
346 Label::primary(fid, self.span).with_message("invalid escape value"),
347 ]),
348 ErrorKind::InvalidNumber => diag.with_labels(vec![
349 Label::primary(fid, self.span).with_message("unable to parse number"),
350 ]),
351 ErrorKind::OutOfRange(kind) => diag
352 .with_message(format!("number is out of range of '{kind}'"))
353 .with_labels(vec![Label::primary(fid, self.span)]),
354 ErrorKind::Wanted { expected, .. } => diag.with_labels(vec![
355 Label::primary(fid, self.span).with_message(format!("expected {expected}")),
356 ]),
357 ErrorKind::MultilineStringKey => diag.with_labels(vec![
358 Label::primary(fid, self.span).with_message("multiline keys are not allowed"),
359 ]),
360 ErrorKind::UnterminatedString => diag.with_labels(vec![
361 Label::primary(fid, self.span).with_message("eof reached before string terminator"),
362 ]),
363 ErrorKind::DuplicateTable { first, .. } => diag.with_labels(vec![
364 Label::secondary(fid, *first).with_message("first table instance"),
365 Label::primary(fid, self.span).with_message("duplicate table"),
366 ]),
367 ErrorKind::InvalidHexEscape(c) => diag.with_labels(vec![
368 Label::primary(fid, self.span)
369 .with_message(format!("invalid hex escape '{}'", Escape(*c))),
370 ]),
371 ErrorKind::UnquotedString => diag.with_labels(vec![
372 Label::primary(fid, self.span).with_message("string is not quoted"),
373 ]),
374 ErrorKind::UnexpectedKeys { keys } => diag
375 .with_message(format!("found {} unexpected keys", keys.len()))
376 .with_labels(
377 keys.iter()
378 .map(|(_name, span)| Label::secondary(fid, *span))
379 .collect(),
380 ),
381 ErrorKind::MissingField(field) => diag
382 .with_message(format!("missing field '{field}'"))
383 .with_labels(vec![
384 Label::primary(fid, self.span).with_message("table with missing field"),
385 ]),
386 ErrorKind::Deprecated { new, .. } => diag
387 .with_message(format!(
388 "deprecated field enountered, '{new}' should be used instead"
389 ))
390 .with_labels(vec![
391 Label::primary(fid, self.span).with_message("deprecated field"),
392 ]),
393 ErrorKind::UnexpectedValue { expected, .. } => diag
394 .with_message(format!("expected '{expected:?}'"))
395 .with_labels(vec![
396 Label::primary(fid, self.span).with_message("unexpected value"),
397 ]),
398 ErrorKind::UnexpectedEof => diag
399 .with_message("unexpected end of file")
400 .with_labels(vec![Label::primary(fid, self.span)]),
401 ErrorKind::DottedKeyInvalidType { first } => {
402 diag.with_message(self.to_string()).with_labels(vec![
403 Label::primary(fid, self.span).with_message("attempted to extend table here"),
404 Label::secondary(fid, *first).with_message("non-table"),
405 ])
406 }
407 ErrorKind::RedefineAsArray => diag
408 .with_message(self.to_string())
409 .with_labels(vec![Label::primary(fid, self.span)]),
410 ErrorKind::Custom(msg) => diag
411 .with_message(msg.to_string())
412 .with_labels(vec![Label::primary(fid, self.span)]),
413 ErrorKind::FileTooLarge => diag
414 .with_message("file is too large (maximum 4GiB)")
415 .with_labels(vec![Label::primary(fid, self.span)]),
416 }
417 }
418}