1#[cfg(feature = "report")]
2mod report;
3
4use std::fmt;
5
6#[cfg(feature = "report")]
7pub use report::Report;
8
9use crate::{
10 lexer,
11 parser::AdditionalErrors,
12 type_system::{DirectiveLocation, MalformedDirectiveLocation},
13 Span,
14};
15
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum Error {
18 InvalidToken { location: usize },
21
22 UnrecognizedEof {
24 location: usize,
26
27 expected: Vec<String>,
31 },
32
33 UnrecognizedToken {
35 token: (usize, String, usize),
37
38 expected: Vec<String>,
42 },
43
44 ExtraToken { token: (usize, String, usize) },
46
47 Lexical(lexer::LexicalError),
49
50 MalformedStringLiteral(crate::common::MalformedStringError),
52
53 MalformedDirectiveLocation(usize, String, usize),
55
56 VariableInConstPosition(usize, String, usize),
58
59 EmptyTypeSystemDocument,
61
62 EmptyExecutableDocument,
64}
65
66impl Error {
67 pub fn span(&self) -> Option<Span> {
68 match self {
69 Error::InvalidToken { location } => Span::new(*location, *location).into(),
70 Error::UnrecognizedEof { location, .. } => Span::new(*location, *location).into(),
71 Error::UnrecognizedToken {
72 token: (start, _, end),
73 ..
74 } => Span::new(*start, *end).into(),
75 Error::ExtraToken {
76 token: (start, _, end),
77 ..
78 } => Span::new(*start, *end).into(),
79 Error::Lexical(error) => error.span().into(),
80 Error::MalformedStringLiteral(error) => error.span().into(),
81 Error::MalformedDirectiveLocation(lhs, _, rhs) => Span::new(*lhs, *rhs).into(),
82 Error::VariableInConstPosition(lhs, _, rhs) => Span::new(*lhs, *rhs).into(),
83 Error::EmptyExecutableDocument | Error::EmptyTypeSystemDocument => None,
84 }
85 }
86}
87
88impl std::error::Error for Error {
89 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90 match self {
91 Error::InvalidToken { .. }
92 | Error::UnrecognizedEof { .. }
93 | Error::UnrecognizedToken { .. }
94 | Error::ExtraToken { .. }
95 | Error::MalformedStringLiteral(..)
96 | Error::MalformedDirectiveLocation(..)
97 | Error::VariableInConstPosition(..)
98 | Error::EmptyTypeSystemDocument
99 | Error::EmptyExecutableDocument => None,
100 Error::Lexical(error) => Some(error),
101 }
102 }
103}
104
105impl fmt::Display for Error {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 Error::InvalidToken { location: _ } => {
109 write!(f, "invalid token")
110 }
111 Error::UnrecognizedEof {
112 location: _,
113 expected,
114 } => {
115 write!(f, "unexpected end of file (expected one of ")?;
116 for (i, item) in expected.iter().enumerate() {
117 if i != 1 {
118 write!(f, ", ")?;
119 }
120 write!(f, "{item}")?;
121 }
122 write!(f, ")")
123 }
124 Error::UnrecognizedToken {
125 token: (_, token, _),
126 expected,
127 } => {
128 write!(f, "unexpected {token} token (expected one of ")?;
129 for (i, item) in expected.iter().enumerate() {
130 if i != 1 {
131 write!(f, ", ")?;
132 }
133 write!(f, "{item}")?;
134 }
135 write!(f, ")")
136 }
137 Error::ExtraToken {
138 token: (_, token, _),
139 } => {
140 write!(f, "found a {token} after the expected end of the document")
141 }
142 Error::Lexical(error) => {
143 write!(f, "{error}")
144 }
145 Error::MalformedStringLiteral(error) => {
146 write!(f, "malformed string literal: {error}")
147 }
148 Error::MalformedDirectiveLocation(_, location, _) => {
149 write!(
150 f,
151 "unknown directive location: {location}. expected one of "
152 )?;
153
154 for (i, location) in DirectiveLocation::all_locations().iter().enumerate() {
155 if i != 0 {
156 write!(f, ", ")?;
157 }
158 write!(f, "{location}")?;
159 }
160 Ok(())
161 }
162 Error::VariableInConstPosition(_, name, _) => {
163 write!(
164 f,
165 "the variable ${name} was found in a position that does not allow variables"
166 )?;
167 Ok(())
168 }
169 Error::EmptyExecutableDocument => {
170 write!(
171 f,
172 "the graphql document was empty, please provide an operation"
173 )
174 }
175 Error::EmptyTypeSystemDocument => {
176 write!(
177 f,
178 "the graphql document was empty, please provide at least one definition"
179 )
180 }
181 }
182 }
183}
184
185impl From<lalrpop_util::ParseError<usize, lexer::Token<'_>, AdditionalErrors>> for Error {
186 fn from(value: lalrpop_util::ParseError<usize, lexer::Token<'_>, AdditionalErrors>) -> Self {
187 use lalrpop_util::ParseError;
188 match value {
189 ParseError::InvalidToken { location } => Error::InvalidToken { location },
190 ParseError::UnrecognizedEof { location, expected } => {
191 Error::UnrecognizedEof { location, expected }
192 }
193 ParseError::UnrecognizedToken {
194 token: (lspan, token, rspan),
195 expected,
196 } => Error::UnrecognizedToken {
197 token: (lspan, token.to_string(), rspan),
198 expected,
199 },
200 ParseError::ExtraToken {
201 token: (lspan, token, rspan),
202 } => Error::ExtraToken {
203 token: (lspan, token.to_string(), rspan),
204 },
205 ParseError::User {
206 error: AdditionalErrors::Lexical(error),
207 } => Error::Lexical(error),
208 ParseError::User {
209 error: AdditionalErrors::MalformedString(error),
210 } => Error::MalformedStringLiteral(error),
211 ParseError::User {
212 error: AdditionalErrors::MalformedDirectiveLocation(lhs, location, rhs),
213 } => Error::MalformedDirectiveLocation(lhs, location, rhs),
214 ParseError::User {
215 error: AdditionalErrors::VariableInConstPosition(lhs, name, rhs),
216 } => Error::MalformedDirectiveLocation(lhs, name, rhs),
217 }
218 }
219}
220
221impl MalformedDirectiveLocation {
222 pub(crate) fn into_lalrpop_error<'a>(
223 self,
224 (lhs, rhs): (usize, usize),
225 ) -> lalrpop_util::ParseError<usize, lexer::Token<'a>, AdditionalErrors> {
226 lalrpop_util::ParseError::User {
227 error: AdditionalErrors::MalformedDirectiveLocation(lhs, self.0, rhs),
228 }
229 }
230}