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