2020-09-06 05:38:31 +00:00
|
|
|
//! A parser for GraphQL. Used in the [`async-graphql`](https://crates.io/crates/async-graphql)
|
|
|
|
//! crate.
|
|
|
|
//!
|
|
|
|
//! It uses the [pest](https://crates.io/crates/pest) crate to parse the input and then transforms
|
|
|
|
//! it into Rust types.
|
2020-10-13 02:19:30 +00:00
|
|
|
#![warn(missing_docs)]
|
2021-02-13 01:55:53 +00:00
|
|
|
#![allow(clippy::unnecessary_wraps)]
|
2021-03-26 13:07:45 +00:00
|
|
|
#![allow(clippy::upper_case_acronyms)]
|
|
|
|
#![allow(clippy::needless_question_mark)]
|
2020-05-29 09:33:19 +00:00
|
|
|
#![forbid(unsafe_code)]
|
|
|
|
|
2020-10-11 12:24:31 +00:00
|
|
|
use crate::types::OperationType;
|
|
|
|
use async_graphql_value::Name;
|
2020-09-06 05:38:31 +00:00
|
|
|
use pest::error::LineColLocation;
|
2020-09-06 06:16:36 +00:00
|
|
|
use pest::RuleType;
|
2020-09-22 18:59:48 +00:00
|
|
|
use serde::{Serialize, Serializer};
|
|
|
|
use std::fmt::{self, Display, Formatter};
|
2020-05-09 14:02:55 +00:00
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
pub use parse::{parse_query, parse_schema};
|
2020-09-06 06:16:36 +00:00
|
|
|
pub use pos::{Pos, Positioned};
|
2020-09-06 05:38:31 +00:00
|
|
|
|
|
|
|
pub mod types;
|
2020-05-15 03:42:01 +00:00
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
mod parse;
|
2020-09-06 06:16:36 +00:00
|
|
|
mod pos;
|
2020-05-09 14:02:55 +00:00
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
/// Parser error.
|
2020-09-22 18:59:48 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum Error {
|
|
|
|
/// A syntax error occurred.
|
|
|
|
Syntax {
|
|
|
|
/// The message of the error, nicely formatted with newlines.
|
|
|
|
message: String,
|
|
|
|
/// The start position of the error.
|
|
|
|
start: Pos,
|
|
|
|
/// The end position of the error, if present.
|
|
|
|
end: Option<Pos>,
|
|
|
|
},
|
|
|
|
/// The schema contained multiple query, mutation or subscription roots.
|
|
|
|
MultipleRoots {
|
|
|
|
/// The type of root that was duplicated.
|
|
|
|
root: OperationType,
|
|
|
|
/// The position of the schema.
|
|
|
|
schema: Pos,
|
|
|
|
/// The position of the second root.
|
|
|
|
pos: Pos,
|
|
|
|
},
|
|
|
|
/// The schema contained no query root.
|
|
|
|
MissingQueryRoot {
|
|
|
|
/// The position of the schema.
|
|
|
|
pos: Pos,
|
|
|
|
},
|
|
|
|
/// Multiple operations were found in a document with an anonymous one.
|
|
|
|
MultipleOperations {
|
|
|
|
/// The position of the anonymous operation.
|
|
|
|
anonymous: Pos,
|
|
|
|
/// The position of the other operation.
|
|
|
|
operation: Pos,
|
|
|
|
},
|
|
|
|
/// An operation is defined multiple times in a document.
|
|
|
|
OperationDuplicated {
|
|
|
|
/// The name of the operation.
|
|
|
|
operation: Name,
|
|
|
|
/// The position of the first definition.
|
|
|
|
first: Pos,
|
|
|
|
/// The position of the second definition.
|
|
|
|
second: Pos,
|
|
|
|
},
|
|
|
|
/// A fragment is defined multiple times in a document.
|
|
|
|
FragmentDuplicated {
|
|
|
|
/// The name of the fragment.
|
|
|
|
fragment: Name,
|
|
|
|
/// The position of the first definition.
|
|
|
|
first: Pos,
|
|
|
|
/// The position of the second definition.
|
|
|
|
second: Pos,
|
|
|
|
},
|
|
|
|
/// The document does not contain any operation.
|
|
|
|
MissingOperation,
|
2020-09-06 05:38:31 +00:00
|
|
|
}
|
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
impl Error {
|
2020-09-22 18:59:48 +00:00
|
|
|
/// Get an iterator over the positions of the error.
|
|
|
|
///
|
|
|
|
/// The iterator is ordered from most important to least important position.
|
|
|
|
#[must_use]
|
|
|
|
pub fn positions(&self) -> ErrorPositions {
|
|
|
|
match self {
|
|
|
|
Self::Syntax {
|
|
|
|
start,
|
|
|
|
end: Some(end),
|
|
|
|
..
|
|
|
|
} => ErrorPositions::new_2(*start, *end),
|
|
|
|
Self::Syntax { start, .. } => ErrorPositions::new_1(*start),
|
|
|
|
Self::MultipleRoots { schema, pos, .. } => ErrorPositions::new_2(*pos, *schema),
|
|
|
|
Self::MissingQueryRoot { pos } => ErrorPositions::new_1(*pos),
|
|
|
|
Self::MultipleOperations {
|
|
|
|
anonymous,
|
|
|
|
operation,
|
|
|
|
} => ErrorPositions::new_2(*anonymous, *operation),
|
|
|
|
Self::OperationDuplicated { first, second, .. } => {
|
|
|
|
ErrorPositions::new_2(*second, *first)
|
|
|
|
}
|
|
|
|
Self::FragmentDuplicated { first, second, .. } => {
|
|
|
|
ErrorPositions::new_2(*second, *first)
|
|
|
|
}
|
|
|
|
Self::MissingOperation => ErrorPositions::new_0(),
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
impl Display for Error {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2021-07-31 15:54:16 +00:00
|
|
|
Self::Syntax { message, .. } => f.write_str(message),
|
2020-09-22 18:59:48 +00:00
|
|
|
Self::MissingQueryRoot { .. } => f.write_str("schema definition is missing query root"),
|
|
|
|
Self::MultipleRoots { root, .. } => {
|
|
|
|
write!(f, "multiple {} roots in schema definition", root)
|
|
|
|
}
|
|
|
|
Self::MultipleOperations { .. } => f.write_str("document contains multiple operations"),
|
|
|
|
Self::OperationDuplicated { operation, .. } => {
|
|
|
|
write!(f, "operation {} is defined twice", operation)
|
|
|
|
}
|
|
|
|
Self::FragmentDuplicated { fragment, .. } => {
|
|
|
|
write!(f, "fragment {} is defined twice", fragment)
|
|
|
|
}
|
|
|
|
Self::MissingOperation => f.write_str("document does not contain an operation"),
|
|
|
|
}
|
2020-09-06 05:38:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
|
|
|
|
impl<R: RuleType> From<pest::error::Error<R>> for Error {
|
|
|
|
fn from(err: pest::error::Error<R>) -> Self {
|
2020-09-22 18:59:48 +00:00
|
|
|
let (start, end) = match err.line_col {
|
|
|
|
LineColLocation::Pos(at) => (at, None),
|
|
|
|
LineColLocation::Span(start, end) => (start, Some(end)),
|
|
|
|
};
|
|
|
|
|
|
|
|
Error::Syntax {
|
2020-09-06 05:38:31 +00:00
|
|
|
message: err.to_string(),
|
2020-09-22 18:59:48 +00:00
|
|
|
start: Pos::from(start),
|
|
|
|
end: end.map(Pos::from),
|
2020-09-06 05:38:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An alias for `Result<T, Error>`.
|
|
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
2020-09-22 18:59:48 +00:00
|
|
|
|
|
|
|
/// An iterator over the positions inside an error.
|
|
|
|
///
|
|
|
|
/// Constructed from the `Error::postions` function.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ErrorPositions(ErrorPositionsInner);
|
|
|
|
|
|
|
|
impl ErrorPositions {
|
|
|
|
fn new_0() -> Self {
|
|
|
|
Self(ErrorPositionsInner::None)
|
|
|
|
}
|
|
|
|
fn new_1(a: Pos) -> Self {
|
|
|
|
Self(ErrorPositionsInner::One(a))
|
|
|
|
}
|
|
|
|
fn new_2(a: Pos, b: Pos) -> Self {
|
|
|
|
Self(ErrorPositionsInner::Two(a, b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for ErrorPositions {
|
|
|
|
type Item = Pos;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
match self.0 {
|
|
|
|
ErrorPositionsInner::Two(a, b) => {
|
|
|
|
self.0 = ErrorPositionsInner::One(b);
|
|
|
|
Some(a)
|
|
|
|
}
|
|
|
|
ErrorPositionsInner::One(a) => {
|
|
|
|
self.0 = ErrorPositionsInner::None;
|
|
|
|
Some(a)
|
|
|
|
}
|
|
|
|
ErrorPositionsInner::None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
let len = self.len();
|
|
|
|
(len, Some(len))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DoubleEndedIterator for ErrorPositions {
|
|
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
|
|
match self.0 {
|
|
|
|
ErrorPositionsInner::Two(a, b) => {
|
|
|
|
self.0 = ErrorPositionsInner::One(a);
|
|
|
|
Some(b)
|
|
|
|
}
|
|
|
|
ErrorPositionsInner::One(a) => {
|
|
|
|
self.0 = ErrorPositionsInner::None;
|
|
|
|
Some(a)
|
|
|
|
}
|
|
|
|
ErrorPositionsInner::None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::iter::FusedIterator for ErrorPositions {}
|
|
|
|
|
|
|
|
impl ExactSizeIterator for ErrorPositions {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
match self.0 {
|
|
|
|
ErrorPositionsInner::Two(_, _) => 2,
|
|
|
|
ErrorPositionsInner::One(_) => 1,
|
|
|
|
ErrorPositionsInner::None => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for ErrorPositions {
|
|
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
|
|
|
serializer.collect_seq(self.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum ErrorPositionsInner {
|
|
|
|
Two(Pos, Pos),
|
|
|
|
One(Pos),
|
|
|
|
None,
|
|
|
|
}
|