shex_compact/
located_parse_error.rs

1use crate::{shex_parser_error::ParseError as ShExParseError, Span};
2use nom::error::{ErrorKind, FromExternalError};
3use std::{
4    fmt::Debug,
5    num::{ParseFloatError, ParseIntError},
6};
7use thiserror::Error;
8
9/// A [`ShExParseError`] at a certain location
10#[derive(Debug, Error)]
11#[error("Parse error on line {}, column {}: {}\nat {}{}",
12  .line, .column,
13  .source,
14  .fragment,
15  format_parse_error_context(.context))]
16pub struct LocatedParseError {
17    #[source]
18    pub source: ShExParseError,
19    pub line: u32,
20    pub column: usize,
21    pub fragment: String,
22    pub context: Vec<LocatedParseError>,
23}
24
25impl LocatedParseError {
26    /// Append another [`LocatedParseError`] as context to this error.
27    pub(crate) fn append(&mut self, other: LocatedParseError) {
28        self.context.push(other)
29    }
30}
31
32pub(crate) fn format_parse_error_context(context: &[LocatedParseError]) -> String {
33    let mut fragments = Vec::new();
34
35    for error in context {
36        let error_string = format!("{error}");
37        for line in error_string.split('\n') {
38            fragments.push(format!("{}{line}", " ".repeat(2)));
39        }
40    }
41
42    if fragments.is_empty() {
43        String::new()
44    } else {
45        format!("\nContext:\n{}", fragments.join("\n"))
46    }
47}
48
49impl nom::error::ParseError<Span<'_>> for LocatedParseError {
50    fn from_error_kind(input: Span, kind: ErrorKind) -> Self {
51        ShExParseError::SyntaxError(kind.description().to_string()).at(input)
52    }
53
54    fn append(input: Span, kind: ErrorKind, other: Self) -> Self {
55        let mut error = ShExParseError::SyntaxError(kind.description().to_string()).at(input);
56        error.append(other);
57        error
58    }
59}
60
61impl FromExternalError<Span<'_>, ParseIntError> for LocatedParseError {
62    fn from_external_error(input: Span, _kind: ErrorKind, e: ParseIntError) -> Self {
63        ShExParseError::ParseIntError {
64            str: input.fragment().to_string(),
65            err: e,
66        }
67        .at(input)
68    }
69}
70
71impl FromExternalError<Span<'_>, ParseFloatError> for LocatedParseError {
72    fn from_external_error(input: Span, _kind: ErrorKind, e: ParseFloatError) -> Self {
73        ShExParseError::ParseFloatError {
74            str: input.fragment().to_string(),
75            err: e,
76        }
77        .at(input)
78    }
79}