1use crate::Span;
2use std::fmt::{self, Debug, Display};
3
4#[derive(Debug, Clone)]
6pub struct Error {
7 pub kind: ErrorKind,
9 pub span: Span,
13 pub line_info: Option<(usize, usize)>,
15}
16
17impl std::error::Error for Error {}
18
19impl From<(ErrorKind, Span)> for Error {
20 fn from((kind, span): (ErrorKind, Span)) -> Self {
21 Self {
22 kind,
23 span,
24 line_info: None,
25 }
26 }
27}
28
29#[derive(Debug, Clone)]
31pub enum ErrorKind {
32 UnexpectedEof,
34
35 InvalidCharInString(char),
37
38 InvalidEscape(char),
40
41 InvalidHexEscape(char),
43
44 InvalidEscapeValue(u32),
48
49 Unexpected(char),
52
53 UnterminatedString,
56
57 InvalidNumber,
59
60 OutOfRange(&'static str),
63
64 Wanted {
66 expected: &'static str,
68 found: &'static str,
70 },
71
72 DuplicateTable {
74 name: String,
76 first: Span,
78 },
79
80 DuplicateKey {
82 key: String,
84 first: Span,
86 },
87
88 RedefineAsArray,
90
91 MultilineStringKey,
93
94 Custom(std::borrow::Cow<'static, str>),
97
98 DottedKeyInvalidType {
100 first: Span,
102 },
103
104 UnexpectedKeys {
108 keys: Vec<(String, Span)>,
110 expected: Vec<String>,
112 },
113
114 UnquotedString,
116
117 MissingField(&'static str),
119
120 Deprecated {
122 old: &'static str,
124 new: &'static str,
126 },
127
128 UnexpectedValue {
130 expected: &'static [&'static str],
132 value: Option<String>,
134 },
135}
136
137impl Display for ErrorKind {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match self {
140 Self::UnexpectedEof => f.write_str("unexpected-eof"),
141 Self::Custom(..) => f.write_str("custom"),
142 Self::DottedKeyInvalidType { .. } => f.write_str("dotted-key-invalid-type"),
143 Self::DuplicateKey { .. } => f.write_str("duplicate-key"),
144 Self::DuplicateTable { .. } => f.write_str("duplicate-table"),
145 Self::UnexpectedKeys { .. } => f.write_str("unexpected-keys"),
146 Self::UnquotedString => f.write_str("unquoted-string"),
147 Self::MultilineStringKey => f.write_str("multiline-string-key"),
148 Self::RedefineAsArray => f.write_str("redefine-as-array"),
149 Self::InvalidCharInString(..) => f.write_str("invalid-char-in-string"),
150 Self::InvalidEscape(..) => f.write_str("invalid-escape"),
151 Self::InvalidEscapeValue(..) => f.write_str("invalid-escape-value"),
152 Self::InvalidHexEscape(..) => f.write_str("invalid-hex-escape"),
153 Self::Unexpected(..) => f.write_str("unexpected"),
154 Self::UnterminatedString => f.write_str("unterminated-string"),
155 Self::InvalidNumber => f.write_str("invalid-number"),
156 Self::OutOfRange(_) => f.write_str("out-of-range"),
157 Self::Wanted { .. } => f.write_str("wanted"),
158 Self::MissingField(..) => f.write_str("missing-field"),
159 Self::Deprecated { .. } => f.write_str("deprecated"),
160 Self::UnexpectedValue { .. } => f.write_str("unexpected-value"),
161 }
162 }
163}
164
165struct Escape(char);
166
167impl fmt::Display for Escape {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 use std::fmt::Write as _;
170
171 if self.0.is_whitespace() {
172 for esc in self.0.escape_default() {
173 f.write_char(esc)?;
174 }
175 Ok(())
176 } else {
177 f.write_char(self.0)
178 }
179 }
180}
181
182impl Display for Error {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match &self.kind {
185 ErrorKind::UnexpectedEof => f.write_str("unexpected eof encountered")?,
186 ErrorKind::InvalidCharInString(c) => {
187 write!(f, "invalid character in string: `{}`", Escape(*c))?;
188 }
189 ErrorKind::InvalidEscape(c) => {
190 write!(f, "invalid escape character in string: `{}`", Escape(*c))?;
191 }
192 ErrorKind::InvalidHexEscape(c) => write!(
193 f,
194 "invalid hex escape character in string: `{}`",
195 Escape(*c)
196 )?,
197 ErrorKind::InvalidEscapeValue(c) => write!(f, "invalid escape value: `{c}`")?,
198 ErrorKind::Unexpected(c) => write!(f, "unexpected character found: `{}`", Escape(*c))?,
199 ErrorKind::UnterminatedString => f.write_str("unterminated string")?,
200 ErrorKind::Wanted { expected, found } => {
201 write!(f, "expected {expected}, found {found}")?;
202 }
203 ErrorKind::InvalidNumber => f.write_str("invalid number")?,
204 ErrorKind::OutOfRange(kind) => write!(f, "out of range of '{kind}'")?,
205 ErrorKind::DuplicateTable { name, .. } => {
206 write!(f, "redefinition of table `{name}`")?;
207 }
208 ErrorKind::DuplicateKey { key, .. } => {
209 write!(f, "duplicate key: `{key}`")?;
210 }
211 ErrorKind::RedefineAsArray => f.write_str("table redefined as array")?,
212 ErrorKind::MultilineStringKey => {
213 f.write_str("multiline strings are not allowed for key")?;
214 }
215 ErrorKind::Custom(message) => f.write_str(message)?,
216 ErrorKind::DottedKeyInvalidType { .. } => {
217 f.write_str("dotted key attempted to extend non-table type")?;
218 }
219 ErrorKind::UnexpectedKeys { keys, expected } => write!(
220 f,
221 "unexpected keys in table: `{keys:?}`\nexpected: {expected:?}"
222 )?,
223 ErrorKind::UnquotedString => {
224 f.write_str("invalid TOML value, did you mean to use a quoted string?")?;
225 }
226 ErrorKind::MissingField(field) => write!(f, "missing field '{field}' in table")?,
227 ErrorKind::Deprecated { old, new } => {
228 write!(f, "field '{old}' is deprecated, '{new}' has replaced it")?;
229 }
230 ErrorKind::UnexpectedValue { expected, .. } => write!(f, "expected '{expected:?}'")?,
231 }
232
233 Ok(())
249 }
250}
251
252#[cfg(feature = "reporting")]
253#[cfg_attr(docsrs, doc(cfg(feature = "reporting")))]
254impl Error {
255 pub fn to_diagnostic<FileId: Copy + PartialEq>(
257 &self,
258 fid: FileId,
259 ) -> codespan_reporting::diagnostic::Diagnostic<FileId> {
260 let diag =
261 codespan_reporting::diagnostic::Diagnostic::error().with_code(self.kind.to_string());
262
263 use codespan_reporting::diagnostic::Label;
264
265 let diag = match &self.kind {
266 ErrorKind::DuplicateKey { first, .. } => diag.with_labels(vec![
267 Label::secondary(fid, *first).with_message("first key instance"),
268 Label::primary(fid, self.span).with_message("duplicate key"),
269 ]),
270 ErrorKind::Unexpected(c) => diag.with_labels(vec![Label::primary(fid, self.span)
271 .with_message(format!("unexpected character '{}'", Escape(*c)))]),
272 ErrorKind::InvalidCharInString(c) => {
273 diag.with_labels(vec![Label::primary(fid, self.span)
274 .with_message(format!("invalid character '{}' in string", Escape(*c)))])
275 }
276 ErrorKind::InvalidEscape(c) => diag.with_labels(vec![Label::primary(fid, self.span)
277 .with_message(format!(
278 "invalid escape character '{}' in string",
279 Escape(*c)
280 ))]),
281 ErrorKind::InvalidEscapeValue(_) => diag
282 .with_labels(vec![
283 Label::primary(fid, self.span).with_message("invalid escape value")
284 ]),
285 ErrorKind::InvalidNumber => diag.with_labels(vec![
286 Label::primary(fid, self.span).with_message("unable to parse number")
287 ]),
288 ErrorKind::OutOfRange(kind) => diag
289 .with_message(format!("number is out of range of '{kind}'"))
290 .with_labels(vec![Label::primary(fid, self.span)]),
291 ErrorKind::Wanted { expected, .. } => diag
292 .with_labels(vec![
293 Label::primary(fid, self.span).with_message(format!("expected {expected}"))
294 ]),
295 ErrorKind::MultilineStringKey => diag.with_labels(vec![
296 Label::primary(fid, self.span).with_message("multiline keys are not allowed")
297 ]),
298 ErrorKind::UnterminatedString => diag
299 .with_labels(vec![Label::primary(fid, self.span)
300 .with_message("eof reached before string terminator")]),
301 ErrorKind::DuplicateTable { first, .. } => diag.with_labels(vec![
302 Label::secondary(fid, *first).with_message("first table instance"),
303 Label::primary(fid, self.span).with_message("duplicate table"),
304 ]),
305 ErrorKind::InvalidHexEscape(c) => diag
306 .with_labels(vec![Label::primary(fid, self.span)
307 .with_message(format!("invalid hex escape '{}'", Escape(*c)))]),
308 ErrorKind::UnquotedString => diag.with_labels(vec![
309 Label::primary(fid, self.span).with_message("string is not quoted")
310 ]),
311 ErrorKind::UnexpectedKeys { keys, expected } => diag
312 .with_message(format!(
313 "found {} unexpected keys, expected: {expected:?}",
314 keys.len()
315 ))
316 .with_labels(
317 keys.iter()
318 .map(|(_name, span)| Label::secondary(fid, *span))
319 .collect(),
320 ),
321 ErrorKind::MissingField(field) => diag
322 .with_message(format!("missing field '{field}'"))
323 .with_labels(vec![
324 Label::primary(fid, self.span).with_message("table with missing field")
325 ]),
326 ErrorKind::Deprecated { new, .. } => diag
327 .with_message(format!(
328 "deprecated field enountered, '{new}' should be used instead"
329 ))
330 .with_labels(vec![
331 Label::primary(fid, self.span).with_message("deprecated field")
332 ]),
333 ErrorKind::UnexpectedValue { expected, .. } => diag
334 .with_message(format!("expected '{expected:?}'"))
335 .with_labels(vec![
336 Label::primary(fid, self.span).with_message("unexpected value")
337 ]),
338 ErrorKind::UnexpectedEof => diag
339 .with_message("unexpected end of file")
340 .with_labels(vec![Label::primary(fid, self.span)]),
341 ErrorKind::DottedKeyInvalidType { first } => {
342 diag.with_message(self.to_string()).with_labels(vec![
343 Label::primary(fid, self.span).with_message("attempted to extend table here"),
344 Label::secondary(fid, *first).with_message("non-table"),
345 ])
346 }
347 ErrorKind::RedefineAsArray => diag
348 .with_message(self.to_string())
349 .with_labels(vec![Label::primary(fid, self.span)]),
350 ErrorKind::Custom(msg) => diag
351 .with_message(msg.to_string())
352 .with_labels(vec![Label::primary(fid, self.span)]),
353 };
354
355 diag
356 }
357}
358
359#[derive(Debug)]
362pub struct DeserError {
363 pub errors: Vec<Error>,
365}
366
367impl DeserError {
368 #[inline]
370 pub fn merge(&mut self, mut other: Self) {
371 self.errors.append(&mut other.errors);
372 }
373}
374
375impl std::error::Error for DeserError {}
376
377impl From<Error> for DeserError {
378 fn from(value: Error) -> Self {
379 Self {
380 errors: vec![value],
381 }
382 }
383}
384
385impl fmt::Display for DeserError {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 for err in &self.errors {
388 writeln!(f, "{err}")?;
389 }
390
391 Ok(())
392 }
393}