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}