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