Coverage Report

Created: 2025-11-28 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/async-graphql-parser-7.0.17/src/lib.rs
Line
Count
Source
1
//! A parser for GraphQL. Used in the [`async-graphql`](https://crates.io/crates/async-graphql)
2
//! crate.
3
//!
4
//! It uses the [pest](https://crates.io/crates/pest) crate to parse the input and then transforms
5
//! it into Rust types.
6
#![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
13
use std::fmt::{self, Display, Formatter};
14
15
use async_graphql_value::Name;
16
pub use parse::{parse_query, parse_schema};
17
use pest::{RuleType, error::LineColLocation};
18
pub use pos::{Pos, Positioned};
19
use serde::{Serialize, Serializer};
20
21
use crate::types::OperationType;
22
23
pub mod types;
24
25
mod parse;
26
mod pos;
27
28
/// Parser error.
29
#[derive(Debug, Clone, PartialEq, Eq)]
30
#[non_exhaustive]
31
pub enum Error {
32
    /// A syntax error occurred.
33
    Syntax {
34
        /// The message of the error, nicely formatted with newlines.
35
        message: String,
36
        /// The start position of the error.
37
        start: Pos,
38
        /// The end position of the error, if present.
39
        end: Option<Pos>,
40
    },
41
    /// The schema contained multiple query, mutation or subscription roots.
42
    MultipleRoots {
43
        /// The type of root that was duplicated.
44
        root: OperationType,
45
        /// The position of the schema.
46
        schema: Pos,
47
        /// The position of the second root.
48
        pos: Pos,
49
    },
50
    /// The schema contained no query root.
51
    MissingQueryRoot {
52
        /// The position of the schema.
53
        pos: Pos,
54
    },
55
    /// Multiple operations were found in a document with an anonymous one.
56
    MultipleOperations {
57
        /// The position of the anonymous operation.
58
        anonymous: Pos,
59
        /// The position of the other operation.
60
        operation: Pos,
61
    },
62
    /// An operation is defined multiple times in a document.
63
    OperationDuplicated {
64
        /// The name of the operation.
65
        operation: Name,
66
        /// The position of the first definition.
67
        first: Pos,
68
        /// The position of the second definition.
69
        second: Pos,
70
    },
71
    /// A fragment is defined multiple times in a document.
72
    FragmentDuplicated {
73
        /// The name of the fragment.
74
        fragment: Name,
75
        /// The position of the first definition.
76
        first: Pos,
77
        /// The position of the second definition.
78
        second: Pos,
79
    },
80
    /// The document does not contain any operation.
81
    MissingOperation,
82
    /// Recursion limit exceeded.
83
    RecursionLimitExceeded,
84
}
85
86
impl Error {
87
    /// Get an iterator over the positions of the error.
88
    ///
89
    /// The iterator is ordered from most important to least important position.
90
    #[must_use]
91
0
    pub fn positions(&self) -> ErrorPositions {
92
0
        match self {
93
            Self::Syntax {
94
0
                start,
95
0
                end: Some(end),
96
                ..
97
0
            } => ErrorPositions::new_2(*start, *end),
98
0
            Self::Syntax { start, .. } => ErrorPositions::new_1(*start),
99
0
            Self::MultipleRoots { schema, pos, .. } => ErrorPositions::new_2(*pos, *schema),
100
0
            Self::MissingQueryRoot { pos } => ErrorPositions::new_1(*pos),
101
            Self::MultipleOperations {
102
0
                anonymous,
103
0
                operation,
104
0
            } => ErrorPositions::new_2(*anonymous, *operation),
105
0
            Self::OperationDuplicated { first, second, .. } => {
106
0
                ErrorPositions::new_2(*second, *first)
107
            }
108
0
            Self::FragmentDuplicated { first, second, .. } => {
109
0
                ErrorPositions::new_2(*second, *first)
110
            }
111
0
            Self::MissingOperation => ErrorPositions::new_0(),
112
0
            Self::RecursionLimitExceeded => ErrorPositions::new_0(),
113
        }
114
0
    }
115
}
116
117
impl Display for Error {
118
0
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
119
0
        match self {
120
0
            Self::Syntax { message, .. } => f.write_str(message),
121
0
            Self::MissingQueryRoot { .. } => f.write_str("schema definition is missing query root"),
122
0
            Self::MultipleRoots { root, .. } => {
123
0
                write!(f, "multiple {} roots in schema definition", root)
124
            }
125
0
            Self::MultipleOperations { .. } => f.write_str("document contains multiple operations"),
126
0
            Self::OperationDuplicated { operation, .. } => {
127
0
                write!(f, "operation {} is defined twice", operation)
128
            }
129
0
            Self::FragmentDuplicated { fragment, .. } => {
130
0
                write!(f, "fragment {} is defined twice", fragment)
131
            }
132
0
            Self::MissingOperation => f.write_str("document does not contain an operation"),
133
0
            Self::RecursionLimitExceeded => f.write_str("recursion limit exceeded."),
134
        }
135
0
    }
136
}
137
138
impl std::error::Error for Error {}
139
140
impl<R: RuleType> From<pest::error::Error<R>> for Error {
141
0
    fn from(err: pest::error::Error<R>) -> Self {
142
0
        let (start, end) = match err.line_col {
143
0
            LineColLocation::Pos(at) => (at, None),
144
0
            LineColLocation::Span(start, end) => (start, Some(end)),
145
        };
146
147
0
        Error::Syntax {
148
0
            message: err.to_string(),
149
0
            start: Pos::from(start),
150
0
            end: end.map(Pos::from),
151
0
        }
152
0
    }
Unexecuted instantiation: <async_graphql_parser::Error as core::convert::From<pest::error::Error<async_graphql_parser::parse::generated::Rule>>>::from
Unexecuted instantiation: <async_graphql_parser::Error as core::convert::From<pest::error::Error<_>>>::from
153
}
154
155
/// An alias for `Result<T, Error>`.
156
pub type Result<T> = std::result::Result<T, Error>;
157
158
/// An iterator over the positions inside an error.
159
///
160
/// Constructed from the `Error::positions` function.
161
#[derive(Debug, Clone)]
162
pub struct ErrorPositions(ErrorPositionsInner);
163
164
impl ErrorPositions {
165
0
    fn new_0() -> Self {
166
0
        Self(ErrorPositionsInner::None)
167
0
    }
168
0
    fn new_1(a: Pos) -> Self {
169
0
        Self(ErrorPositionsInner::One(a))
170
0
    }
171
0
    fn new_2(a: Pos, b: Pos) -> Self {
172
0
        Self(ErrorPositionsInner::Two(a, b))
173
0
    }
174
}
175
176
impl Iterator for ErrorPositions {
177
    type Item = Pos;
178
179
0
    fn next(&mut self) -> Option<Self::Item> {
180
0
        match self.0 {
181
0
            ErrorPositionsInner::Two(a, b) => {
182
0
                self.0 = ErrorPositionsInner::One(b);
183
0
                Some(a)
184
            }
185
0
            ErrorPositionsInner::One(a) => {
186
0
                self.0 = ErrorPositionsInner::None;
187
0
                Some(a)
188
            }
189
0
            ErrorPositionsInner::None => None,
190
        }
191
0
    }
192
193
0
    fn size_hint(&self) -> (usize, Option<usize>) {
194
0
        let len = self.len();
195
0
        (len, Some(len))
196
0
    }
197
}
198
199
impl DoubleEndedIterator for ErrorPositions {
200
0
    fn next_back(&mut self) -> Option<Self::Item> {
201
0
        match self.0 {
202
0
            ErrorPositionsInner::Two(a, b) => {
203
0
                self.0 = ErrorPositionsInner::One(a);
204
0
                Some(b)
205
            }
206
0
            ErrorPositionsInner::One(a) => {
207
0
                self.0 = ErrorPositionsInner::None;
208
0
                Some(a)
209
            }
210
0
            ErrorPositionsInner::None => None,
211
        }
212
0
    }
213
}
214
215
impl std::iter::FusedIterator for ErrorPositions {}
216
217
impl ExactSizeIterator for ErrorPositions {
218
0
    fn len(&self) -> usize {
219
0
        match self.0 {
220
0
            ErrorPositionsInner::Two(_, _) => 2,
221
0
            ErrorPositionsInner::One(_) => 1,
222
0
            ErrorPositionsInner::None => 0,
223
        }
224
0
    }
225
}
226
227
impl Serialize for ErrorPositions {
228
0
    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
229
0
        serializer.collect_seq(self.clone())
230
0
    }
231
}
232
233
#[derive(Debug, Clone, Copy)]
234
enum ErrorPositionsInner {
235
    Two(Pos, Pos),
236
    One(Pos),
237
    None,
238
}