apollo_parser/
error.rs

1use std::fmt;
2
3/// An `Error` type for operations performed in the lexer and the parser.
4///
5/// Errors get returned alongside the resulting CST if either the lexer or the
6/// parser encouter lexical or syntactical errors respectively.
7///
8/// We encourage you to check for the CST's errors before proceeding to iterate
9/// over the CST's nodes:
10///
11/// ## Example
12/// ```rust
13/// use apollo_parser::Parser;
14///
15/// let input = "union SearchResult = Photo | Person | Cat | Dog";
16/// let parser = Parser::new(input);
17/// let cst = parser.parse();
18///
19/// assert_eq!(0, cst.errors().len());
20///
21/// let doc = cst.document();
22/// ```
23///
24/// ### Diagnostics
25///
26/// Using something like [miette crate] along with apollo-parser lets you have
27/// more visual diagnostics. [miette] and [annotate_snippets] examples guide you
28/// through integrating them with apollo-parser. These are useful if you are
29/// displaying Errors in a terminal-like environment.
30///
31/// <img src="https://raw.githubusercontent.com/apollographql/apollo-rs/main/crates/apollo-parser/screenshots/apollo_parser_error.png" alt="A screenshot of an error example produced by using apollo-parser and miette. The ascii display shows a graphql code snippet with line numbers to the left. Under the code sample there is a line pointing to where a value is missing in graphql code">
32///
33/// [miette]: https://github.com/apollographql/apollo-rs/blob/a7f616454a53dcb8496725ceac6c63eacddefb2c/crates/apollo-parser/examples/miette.rs
34/// [annotate_snippets]: https://github.com/apollographql/apollo-rs/blob/a7f616454a53dcb8496725ceac6c63eacddefb2c/crates/apollo-parser/examples/annotate_snippet.rs
35/// [miette crate]: https://docs.rs/miette/3.2.0/miette/index.html
36
37#[derive(Debug, PartialEq, Eq, Clone, Hash)]
38pub(crate) enum ErrorData {
39    Eof,
40    LimitExceeded,
41    Text(String),
42}
43
44impl ErrorData {
45    pub fn len(&self) -> usize {
46        match self {
47            Self::Eof | Self::LimitExceeded => 0,
48            Self::Text(text) => text.len(),
49        }
50    }
51}
52
53impl fmt::Display for ErrorData {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            Self::Eof => write!(f, "EOF"),
57            Self::LimitExceeded => Ok(()),
58            Self::Text(text) => write!(f, "{text}"),
59        }
60    }
61}
62
63#[derive(PartialEq, Eq, Clone, Hash, thiserror::Error)]
64#[error("ERROR@{index}:{} {message:?} {data}", .index + .data.len())]
65pub struct Error {
66    pub(crate) message: String,
67    pub(crate) data: ErrorData,
68    pub(crate) index: usize,
69}
70
71impl Error {
72    /// Create a new instance of `Error` with a `Location`.
73    pub fn with_loc<S: Into<String>>(message: S, data: String, index: usize) -> Self {
74        Self {
75            message: message.into(),
76            data: ErrorData::Text(data),
77            index,
78        }
79    }
80
81    pub fn limit<S: Into<String>>(message: S, index: usize) -> Self {
82        Self {
83            message: message.into(),
84            data: ErrorData::LimitExceeded,
85            index,
86        }
87    }
88
89    pub fn eof<S: Into<String>>(message: S, index: usize) -> Self {
90        Self {
91            message: message.into(),
92            data: ErrorData::Eof,
93            index,
94        }
95    }
96
97    /// Get a reference to the error's data. This is usually the token that
98    /// `apollo-parser` has found to be lexically or syntactically incorrect.
99    pub fn data(&self) -> &str {
100        match &self.data {
101            ErrorData::Text(text) => text,
102            _ => "",
103        }
104    }
105
106    pub fn is_limit(&self) -> bool {
107        matches!(&self.data, ErrorData::LimitExceeded)
108    }
109
110    pub fn is_eof(&self) -> bool {
111        matches!(&self.data, ErrorData::Eof)
112    }
113
114    pub(crate) fn set_data(&mut self, data: String) {
115        self.data = ErrorData::Text(data);
116    }
117
118    /// Get a reference to the error's index. This is where the error begins in
119    /// a given input.
120    pub fn index(&self) -> usize {
121        self.index
122    }
123
124    /// Get a reference to the error's message.
125    pub fn message(&self) -> &str {
126        self.message.as_ref()
127    }
128}
129
130impl fmt::Debug for Error {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        let start = self.index;
133        let end = self.index + self.data.len();
134
135        write!(
136            f,
137            "ERROR@{}:{} {:?} {}",
138            start, end, self.message, self.data
139        )
140    }
141}