1use std::error::Error as StdError;
2use std::fmt::{Display, Formatter, Result};
3
4use crate::parser::prelude::*;
5use crate::Key;
6
7use winnow::BStr;
8
9/// Type representing a TOML parse error
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub struct TomlError {
12 message: String,
13 original: Option<String>,
14 keys: Vec<String>,
15 span: Option<std::ops::Range<usize>>,
16}
17
18impl TomlError {
19 pub(crate) fn new(error: ParserError<'_>, original: Input<'_>) -> Self {
20 use winnow::stream::Offset;
21 use winnow::stream::Stream;
22
23 let offset = original.offset_to(&error.input);
24 let span = if offset == original.len() {
25 offset..offset
26 } else {
27 offset..(offset + 1)
28 };
29
30 let message = error.to_string();
31 let original = original.next_slice(original.eof_offset()).1;
32
33 Self {
34 message,
35 original: Some(
36 String::from_utf8(original.to_owned()).expect("original document was utf8"),
37 ),
38 keys: Vec::new(),
39 span: Some(span),
40 }
41 }
42
43 #[cfg(feature = "serde")]
44 pub(crate) fn custom(message: String, span: Option<std::ops::Range<usize>>) -> Self {
45 Self {
46 message,
47 original: None,
48 keys: Vec::new(),
49 span,
50 }
51 }
52
53 #[cfg(feature = "serde")]
54 pub(crate) fn add_key(&mut self, key: String) {
55 self.keys.insert(0, key);
56 }
57
58 /// What went wrong
59 pub fn message(&self) -> &str {
60 &self.message
61 }
62
63 /// The start/end index into the original document where the error occurred
64 pub fn span(&self) -> Option<std::ops::Range<usize>> {
65 self.span.clone()
66 }
67
68 #[cfg(feature = "serde")]
69 pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
70 self.span = span;
71 }
72
73 #[cfg(feature = "serde")]
74 pub(crate) fn set_original(&mut self, original: Option<String>) {
75 self.original = original;
76 }
77}
78
79/// Displays a TOML parse error
80///
81/// # Example
82///
83/// TOML parse error at line 1, column 10
84/// |
85/// 1 | 00:32:00.a999999
86/// | ^
87/// Unexpected `a`
88/// Expected `digit`
89/// While parsing a Time
90/// While parsing a Date-Time
91impl Display for TomlError {
92 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
93 let mut context = false;
94 if let (Some(original), Some(span)) = (&self.original, self.span()) {
95 context = true;
96
97 let (line, column) = translate_position(original.as_bytes(), span.start);
98 let line_num = line + 1;
99 let col_num = column + 1;
100 let gutter = line_num.to_string().len();
101 let content = original.split('\n').nth(line).expect("valid line number");
102
103 writeln!(
104 f,
105 "TOML parse error at line {}, column {}",
106 line_num, col_num
107 )?;
108 // |
109 for _ in 0..=gutter {
110 write!(f, " ")?;
111 }
112 writeln!(f, "|")?;
113
114 // 1 | 00:32:00.a999999
115 write!(f, "{} | ", line_num)?;
116 writeln!(f, "{}", content)?;
117
118 // | ^
119 for _ in 0..=gutter {
120 write!(f, " ")?;
121 }
122 write!(f, "|")?;
123 for _ in 0..=column {
124 write!(f, " ")?;
125 }
126 // The span will be empty at eof, so we need to make sure we always print at least
127 // one `^`
128 write!(f, "^")?;
129 for _ in (span.start + 1)..(span.end.min(span.start + content.len())) {
130 write!(f, "^")?;
131 }
132 writeln!(f)?;
133 }
134 writeln!(f, "{}", self.message)?;
135 if !context && !self.keys.is_empty() {
136 writeln!(f, "in `{}`", self.keys.join("."))?;
137 }
138
139 Ok(())
140 }
141}
142
143impl StdError for TomlError {
144 fn description(&self) -> &'static str {
145 "TOML parse error"
146 }
147}
148
149#[derive(Debug)]
150pub(crate) struct ParserError<'b> {
151 input: Input<'b>,
152 context: Vec<Context>,
153 cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
154}
155
156impl<'b> winnow::error::ParseError<Input<'b>> for ParserError<'b> {
157 fn from_error_kind(input: Input<'b>, _kind: winnow::error::ErrorKind) -> Self {
158 Self {
159 input,
160 context: Default::default(),
161 cause: Default::default(),
162 }
163 }
164
165 fn append(self, _input: Input<'b>, _kind: winnow::error::ErrorKind) -> Self {
166 self
167 }
168
169 fn or(self, other: Self) -> Self {
170 other
171 }
172}
173
174impl<'b> winnow::error::ParseError<&'b str> for ParserError<'b> {
175 fn from_error_kind(input: &'b str, _kind: winnow::error::ErrorKind) -> Self {
176 Self {
177 input: Input::new(input:BStr::new(bytes:input)),
178 context: Default::default(),
179 cause: Default::default(),
180 }
181 }
182
183 fn append(self, _input: &'b str, _kind: winnow::error::ErrorKind) -> Self {
184 self
185 }
186
187 fn or(self, other: Self) -> Self {
188 other
189 }
190}
191
192impl<'b> winnow::error::ContextError<Input<'b>, Context> for ParserError<'b> {
193 fn add_context(mut self, _input: Input<'b>, ctx: Context) -> Self {
194 self.context.push(ctx);
195 self
196 }
197}
198
199impl<'b, E: std::error::Error + Send + Sync + 'static>
200 winnow::error::FromExternalError<Input<'b>, E> for ParserError<'b>
201{
202 fn from_external_error(input: Input<'b>, _kind: winnow::error::ErrorKind, e: E) -> Self {
203 Self {
204 input,
205 context: Default::default(),
206 cause: Some(Box::new(e)),
207 }
208 }
209}
210
211impl<'b, E: std::error::Error + Send + Sync + 'static> winnow::error::FromExternalError<&'b str, E>
212 for ParserError<'b>
213{
214 fn from_external_error(input: &'b str, _kind: winnow::error::ErrorKind, e: E) -> Self {
215 Self {
216 input: Input::new(input:BStr::new(bytes:input)),
217 context: Default::default(),
218 cause: Some(Box::new(e)),
219 }
220 }
221}
222
223// For tests
224impl<'b> std::cmp::PartialEq for ParserError<'b> {
225 fn eq(&self, other: &Self) -> bool {
226 self.input == other.input
227 && self.context == other.context
228 && self.cause.as_ref().map(ToString::to_string)
229 == other.cause.as_ref().map(ToString::to_string)
230 }
231}
232
233impl<'a> std::fmt::Display for ParserError<'a> {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 let expression = self.context.iter().find_map(|c| match c {
236 Context::Expression(c) => Some(c),
237 _ => None,
238 });
239 let expected = self
240 .context
241 .iter()
242 .filter_map(|c| match c {
243 Context::Expected(c) => Some(c),
244 _ => None,
245 })
246 .collect::<Vec<_>>();
247
248 let mut newline = false;
249
250 if let Some(expression) = expression {
251 newline = true;
252
253 write!(f, "invalid {}", expression)?;
254 }
255
256 if !expected.is_empty() {
257 if newline {
258 writeln!(f)?;
259 }
260 newline = true;
261
262 write!(f, "expected ")?;
263 for (i, expected) in expected.iter().enumerate() {
264 if i != 0 {
265 write!(f, ", ")?;
266 }
267 write!(f, "{}", expected)?;
268 }
269 }
270 if let Some(cause) = &self.cause {
271 if newline {
272 writeln!(f)?;
273 }
274 write!(f, "{}", cause)?;
275 }
276
277 Ok(())
278 }
279}
280
281#[derive(Copy, Clone, Debug, PartialEq)]
282pub(crate) enum Context {
283 Expression(&'static str),
284 Expected(ParserValue),
285}
286
287#[derive(Copy, Clone, Debug, PartialEq)]
288pub(crate) enum ParserValue {
289 CharLiteral(char),
290 StringLiteral(&'static str),
291 Description(&'static str),
292}
293
294impl std::fmt::Display for ParserValue {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 match self {
297 ParserValue::CharLiteral('\n') => "newline".fmt(f),
298 ParserValue::CharLiteral('`') => "'`'".fmt(f),
299 ParserValue::CharLiteral(c: &char) if c.is_ascii_control() => {
300 write!(f, "`{}`", c.escape_debug())
301 }
302 ParserValue::CharLiteral(c: &char) => write!(f, "`{}`", c),
303 ParserValue::StringLiteral(c: &&str) => write!(f, "`{}`", c),
304 ParserValue::Description(c: &&str) => write!(f, "{}", c),
305 }
306 }
307}
308
309fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
310 if input.is_empty() {
311 return (0, index);
312 }
313
314 let safe_index = index.min(input.len() - 1);
315 let column_offset = index - safe_index;
316 let index = safe_index;
317
318 let nl = input[0..index]
319 .iter()
320 .rev()
321 .enumerate()
322 .find(|(_, b)| **b == b'\n')
323 .map(|(nl, _)| index - nl - 1);
324 let line_start = match nl {
325 Some(nl) => nl + 1,
326 None => 0,
327 };
328 let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
329 let line = line;
330
331 let column = std::str::from_utf8(&input[line_start..=index])
332 .map(|s| s.chars().count() - 1)
333 .unwrap_or_else(|_| index - line_start);
334 let column = column + column_offset;
335
336 (line, column)
337}
338
339#[cfg(test)]
340mod test_translate_position {
341 use super::*;
342
343 #[test]
344 fn empty() {
345 let input = b"";
346 let index = 0;
347 let position = translate_position(&input[..], index);
348 assert_eq!(position, (0, 0));
349 }
350
351 #[test]
352 fn start() {
353 let input = b"Hello";
354 let index = 0;
355 let position = translate_position(&input[..], index);
356 assert_eq!(position, (0, 0));
357 }
358
359 #[test]
360 fn end() {
361 let input = b"Hello";
362 let index = input.len() - 1;
363 let position = translate_position(&input[..], index);
364 assert_eq!(position, (0, input.len() - 1));
365 }
366
367 #[test]
368 fn after() {
369 let input = b"Hello";
370 let index = input.len();
371 let position = translate_position(&input[..], index);
372 assert_eq!(position, (0, input.len()));
373 }
374
375 #[test]
376 fn first_line() {
377 let input = b"Hello\nWorld\n";
378 let index = 2;
379 let position = translate_position(&input[..], index);
380 assert_eq!(position, (0, 2));
381 }
382
383 #[test]
384 fn end_of_line() {
385 let input = b"Hello\nWorld\n";
386 let index = 5;
387 let position = translate_position(&input[..], index);
388 assert_eq!(position, (0, 5));
389 }
390
391 #[test]
392 fn start_of_second_line() {
393 let input = b"Hello\nWorld\n";
394 let index = 6;
395 let position = translate_position(&input[..], index);
396 assert_eq!(position, (1, 0));
397 }
398
399 #[test]
400 fn second_line() {
401 let input = b"Hello\nWorld\n";
402 let index = 8;
403 let position = translate_position(&input[..], index);
404 assert_eq!(position, (1, 2));
405 }
406}
407
408#[derive(Debug, Clone)]
409pub(crate) enum CustomError {
410 DuplicateKey {
411 key: String,
412 table: Option<Vec<Key>>,
413 },
414 DottedKeyExtendWrongType {
415 key: Vec<Key>,
416 actual: &'static str,
417 },
418 OutOfRange,
419 #[cfg_attr(feature = "unbounded", allow(dead_code))]
420 RecursionLimitExceeded,
421}
422
423impl CustomError {
424 pub(crate) fn duplicate_key(path: &[Key], i: usize) -> Self {
425 assert!(i < path.len());
426 let key: &Key = &path[i];
427 let repr: Cow<'_, str> = key.display_repr();
428 Self::DuplicateKey {
429 key: repr.into(),
430 table: Some(path[..i].to_vec()),
431 }
432 }
433
434 pub(crate) fn extend_wrong_type(path: &[Key], i: usize, actual: &'static str) -> Self {
435 assert!(i < path.len());
436 Self::DottedKeyExtendWrongType {
437 key: path[..=i].to_vec(),
438 actual,
439 }
440 }
441}
442
443impl StdError for CustomError {
444 fn description(&self) -> &'static str {
445 "TOML parse error"
446 }
447}
448
449impl Display for CustomError {
450 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
451 match self {
452 CustomError::DuplicateKey { key, table } => {
453 if let Some(table) = table {
454 if table.is_empty() {
455 write!(f, "duplicate key `{}` in document root", key)
456 } else {
457 let path = table.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
458 write!(f, "duplicate key `{}` in table `{}`", key, path)
459 }
460 } else {
461 write!(f, "duplicate key `{}`", key)
462 }
463 }
464 CustomError::DottedKeyExtendWrongType { key, actual } => {
465 let path = key.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
466 write!(
467 f,
468 "dotted key `{}` attempted to extend non-table type ({})",
469 path, actual
470 )
471 }
472 CustomError::OutOfRange => write!(f, "value is out of range"),
473 CustomError::RecursionLimitExceeded => write!(f, "recursion limit exceded"),
474 }
475 }
476}
477