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