1#![warn(missing_docs)]
7#![allow(clippy::unnecessary_wraps)]
8#![allow(clippy::upper_case_acronyms)]
9#![allow(clippy::needless_question_mark)]
10#![allow(clippy::uninlined_format_args)]
11#![forbid(unsafe_code)]
12
13use std::fmt::{self, Display, Formatter};
14
15use async_graphql_value::Name;
16pub use parse::{parse_query, parse_schema};
17use pest::{error::LineColLocation, RuleType};
18pub use pos::{Pos, Positioned};
19use serde::{Serialize, Serializer};
20
21use crate::types::OperationType;
22
23pub mod types;
24
25mod parse;
26mod pos;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30#[non_exhaustive]
31pub enum Error {
32 Syntax {
34 message: String,
36 start: Pos,
38 end: Option<Pos>,
40 },
41 MultipleRoots {
43 root: OperationType,
45 schema: Pos,
47 pos: Pos,
49 },
50 MissingQueryRoot {
52 pos: Pos,
54 },
55 MultipleOperations {
57 anonymous: Pos,
59 operation: Pos,
61 },
62 OperationDuplicated {
64 operation: Name,
66 first: Pos,
68 second: Pos,
70 },
71 FragmentDuplicated {
73 fragment: Name,
75 first: Pos,
77 second: Pos,
79 },
80 MissingOperation,
82 RecursionLimitExceeded,
84}
85
86impl Error {
87 #[must_use]
91 pub fn positions(&self) -> ErrorPositions {
92 match self {
93 Self::Syntax {
94 start,
95 end: Some(end),
96 ..
97 } => ErrorPositions::new_2(*start, *end),
98 Self::Syntax { start, .. } => ErrorPositions::new_1(*start),
99 Self::MultipleRoots { schema, pos, .. } => ErrorPositions::new_2(*pos, *schema),
100 Self::MissingQueryRoot { pos } => ErrorPositions::new_1(*pos),
101 Self::MultipleOperations {
102 anonymous,
103 operation,
104 } => ErrorPositions::new_2(*anonymous, *operation),
105 Self::OperationDuplicated { first, second, .. } => {
106 ErrorPositions::new_2(*second, *first)
107 }
108 Self::FragmentDuplicated { first, second, .. } => {
109 ErrorPositions::new_2(*second, *first)
110 }
111 Self::MissingOperation => ErrorPositions::new_0(),
112 Self::RecursionLimitExceeded => ErrorPositions::new_0(),
113 }
114 }
115}
116
117impl Display for Error {
118 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::Syntax { message, .. } => f.write_str(message),
121 Self::MissingQueryRoot { .. } => f.write_str("schema definition is missing query root"),
122 Self::MultipleRoots { root, .. } => {
123 write!(f, "multiple {} roots in schema definition", root)
124 }
125 Self::MultipleOperations { .. } => f.write_str("document contains multiple operations"),
126 Self::OperationDuplicated { operation, .. } => {
127 write!(f, "operation {} is defined twice", operation)
128 }
129 Self::FragmentDuplicated { fragment, .. } => {
130 write!(f, "fragment {} is defined twice", fragment)
131 }
132 Self::MissingOperation => f.write_str("document does not contain an operation"),
133 Self::RecursionLimitExceeded => f.write_str("recursion limit exceeded."),
134 }
135 }
136}
137
138impl std::error::Error for Error {}
139
140impl<R: RuleType> From<pest::error::Error<R>> for Error {
141 fn from(err: pest::error::Error<R>) -> Self {
142 let (start, end) = match err.line_col {
143 LineColLocation::Pos(at) => (at, None),
144 LineColLocation::Span(start, end) => (start, Some(end)),
145 };
146
147 Error::Syntax {
148 message: err.to_string(),
149 start: Pos::from(start),
150 end: end.map(Pos::from),
151 }
152 }
153}
154
155pub type Result<T> = std::result::Result<T, Error>;
157
158#[derive(Debug, Clone)]
162pub struct ErrorPositions(ErrorPositionsInner);
163
164impl ErrorPositions {
165 fn new_0() -> Self {
166 Self(ErrorPositionsInner::None)
167 }
168 fn new_1(a: Pos) -> Self {
169 Self(ErrorPositionsInner::One(a))
170 }
171 fn new_2(a: Pos, b: Pos) -> Self {
172 Self(ErrorPositionsInner::Two(a, b))
173 }
174}
175
176impl Iterator for ErrorPositions {
177 type Item = Pos;
178
179 fn next(&mut self) -> Option<Self::Item> {
180 match self.0 {
181 ErrorPositionsInner::Two(a, b) => {
182 self.0 = ErrorPositionsInner::One(b);
183 Some(a)
184 }
185 ErrorPositionsInner::One(a) => {
186 self.0 = ErrorPositionsInner::None;
187 Some(a)
188 }
189 ErrorPositionsInner::None => None,
190 }
191 }
192
193 fn size_hint(&self) -> (usize, Option<usize>) {
194 let len = self.len();
195 (len, Some(len))
196 }
197}
198
199impl DoubleEndedIterator for ErrorPositions {
200 fn next_back(&mut self) -> Option<Self::Item> {
201 match self.0 {
202 ErrorPositionsInner::Two(a, b) => {
203 self.0 = ErrorPositionsInner::One(a);
204 Some(b)
205 }
206 ErrorPositionsInner::One(a) => {
207 self.0 = ErrorPositionsInner::None;
208 Some(a)
209 }
210 ErrorPositionsInner::None => None,
211 }
212 }
213}
214
215impl std::iter::FusedIterator for ErrorPositions {}
216
217impl ExactSizeIterator for ErrorPositions {
218 fn len(&self) -> usize {
219 match self.0 {
220 ErrorPositionsInner::Two(_, _) => 2,
221 ErrorPositionsInner::One(_) => 1,
222 ErrorPositionsInner::None => 0,
223 }
224 }
225}
226
227impl Serialize for ErrorPositions {
228 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
229 serializer.collect_seq(self.clone())
230 }
231}
232
233#[derive(Debug, Clone, Copy)]
234enum ErrorPositionsInner {
235 Two(Pos, Pos),
236 One(Pos),
237 None,
238}