1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Error types for the [`query`](`crate::query`) module.
//!
//! The main error type is [`ParseErrorReport`], which contains
//! all [`ParseErrors`](`ParseError`) encountered during parsing.
//!
//! # Examples
//!
//! Retrieving the part of input that caused a parse error:
//!
//! ```rust
//! use rsonpath_lib::query::JsonPathQuery;
//! use rsonpath_lib::query::error::ParserError;
//!
//! let query_str =
//!     "$.prop..invalid$chars.this_is_fine";
//! //                  ^     ^
//! //                  |_____|________________ start_idx of the error
//! //                        |________________ at this point the parser recovers
//! //                  ^^^^^^_________________ error length of 6
//! let result = JsonPathQuery::parse(query_str);
//!
//! match result {
//!     Err(ParserError::SyntaxError { report }) => {
//!         assert_eq!(report.errors().count(), 1);
//!         let parse_error = report.errors().next().unwrap();
//!         assert_eq!(parse_error.start_idx, 15);
//!         assert_eq!(parse_error.len, 6);
//!         let start = parse_error.start_idx;
//!         let end = parse_error.start_idx + parse_error.len;
//!         let invalid_tokens = &query_str[start..end];
//!         assert_eq!(invalid_tokens, "$chars");
//!     },
//!     _ => unreachable!(),
//! }
//! ```
use std::{
    fmt::{self, Display},
    num::TryFromIntError,
};
use thiserror::Error;

/// Errors raised by the query parser.
#[derive(Debug, Error)]
pub enum ParserError {
    /// Parsing error that occurred due to invalid input.
    #[error("one or more parsing errors occurred:\n{}", .report)]
    SyntaxError {
        /// Error report.
        report: ParseErrorReport,
    },
    /// Internal parser error. This is not expected to happen,
    /// and signifies a bug in [`query`](`crate::query`).
    #[error(
        "unexpected error in the parser; please report this issue at {}",
        crate::error::BUG_REPORT_URL
    )]
    InternalNomError {
        /// Source error from the [`nom`] crate.
        #[from]
        #[source]
        source: nom::error::Error<String>,
    },
}

/// Error report created during the parser's run over a single input string.
#[derive(Debug)]
pub struct ParseErrorReport {
    errors: Vec<ParseError>,
}

impl Display for ParseErrorReport {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for error in self.errors() {
            writeln!(f, "{error}\n")?;
        }

        Ok(())
    }
}

/// Single error raised during parsing, defined as the
/// contiguous sequence of characters that caused the error.
#[derive(Debug)]
pub struct ParseError {
    /// The index at which the error occurred.
    pub start_idx: usize,
    /// The number of characters that the parser recognised as invalid.
    pub len: usize,
}

impl Display for ParseError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "invalid tokens of length {} at position {} ",
            self.len, self.start_idx
        )
    }
}

impl ParseError {
    fn end_idx(&self) -> usize {
        self.start_idx + self.len - 1
    }
}

impl ParseErrorReport {
    pub(crate) fn new() -> Self {
        Self { errors: vec![] }
    }

    pub(crate) fn record_at(&mut self, idx: usize) {
        match self.errors.last_mut() {
            Some(last_error) if last_error.end_idx() + 1 == idx => last_error.len += 1,
            _ => self.add_new(idx),
        }
    }

    /// Retrieves an [`Iterator`] over all [`ParseErrors`](`ParseError`)
    /// in the report.
    #[inline]
    pub fn errors(&self) -> impl Iterator<Item = &ParseError> {
        self.errors.iter()
    }

    fn add_new(&mut self, idx: usize) {
        self.errors.push(ParseError {
            start_idx: idx,
            len: 1,
        })
    }
}

/// Errors raised by the query compiler.
#[derive(Debug, Error)]
pub enum CompilerError {
    /// Max automaton size was exceeded during compilation of the query.
    #[error("Max automaton size was exceeded. Query is too complex.")]
    QueryTooComplex(#[source] Option<TryFromIntError>),
    /// Compiler error that occurred due to a known limitation.
    #[error(transparent)]
    NotSupported(#[from] crate::error::UnsupportedFeatureError),
}