bluejay_parser/ast/executable/
executable_document.rs1use crate::ast::executable::{
2 ExecutableDefinition, ExplicitOperationDefinition, Field, FragmentDefinition, FragmentSpread,
3 ImplicitOperationDefinition, InlineFragment, OperationDefinition, Selection, SelectionSet,
4 VariableDefinition, VariableDefinitions, VariableType,
5};
6use crate::ast::{
7 Argument, Arguments, DepthLimiter, Directive, Directives, Parse, ParseError, Tokens,
8 TryFromTokens, Value,
9};
10use crate::Error;
11
12#[derive(Debug)]
13pub struct ExecutableDocument<'a> {
14 operation_definitions: Vec<OperationDefinition<'a>>,
15 fragment_definitions: Vec<FragmentDefinition<'a>>,
16}
17
18impl<'a> ExecutableDocument<'a> {
19 pub(crate) fn new(
20 operation_definitions: Vec<OperationDefinition<'a>>,
21 fragment_definitions: Vec<FragmentDefinition<'a>>,
22 ) -> Self {
23 Self {
24 operation_definitions,
25 fragment_definitions,
26 }
27 }
28
29 pub fn operation_definitions(&self) -> &[OperationDefinition<'a>] {
30 &self.operation_definitions
31 }
32
33 pub fn fragment_definitions(&self) -> &[FragmentDefinition<'a>] {
34 &self.fragment_definitions
35 }
36
37 #[inline]
38 fn is_empty(&self) -> bool {
39 self.operation_definitions.is_empty() && self.fragment_definitions.is_empty()
40 }
41}
42
43impl<'a> Parse<'a> for ExecutableDocument<'a> {
44 #[inline]
45 fn parse_from_tokens(
46 mut tokens: impl Tokens<'a>,
47 max_depth: usize,
48 ) -> Result<Self, Vec<Error>> {
49 let mut instance: Self = Self::new(Vec::new(), Vec::new());
50 let mut errors = Vec::new();
51 let mut last_pass_had_error = false;
52
53 loop {
54 last_pass_had_error = if let Some(res) =
55 ExecutableDefinition::try_from_tokens(&mut tokens, DepthLimiter::new(max_depth))
56 {
57 match res {
58 Ok(ExecutableDefinition::Operation(operation_definition)) => {
59 instance.operation_definitions.push(operation_definition);
60 false
61 }
62 Ok(ExecutableDefinition::Fragment(fragment_definition)) => {
63 instance.fragment_definitions.push(fragment_definition);
64 false
65 }
66 Err(ParseError::MaxDepthExceeded) => {
67 errors.push(ParseError::MaxDepthExceeded);
68 break;
70 }
71 Err(err) => {
72 if !last_pass_had_error {
73 errors.push(err);
74 }
75 true
76 }
77 }
78 } else if let Some(token) = tokens.next() {
79 if !last_pass_had_error {
80 errors.push(ParseError::UnexpectedToken { span: token.into() });
81 }
82 true
83 } else {
84 break;
85 }
86 }
87
88 let lex_errors = tokens.into_errors();
89
90 let errors = if lex_errors.is_empty() {
91 if errors.is_empty() && instance.is_empty() {
92 vec![ParseError::EmptyDocument.into()]
93 } else {
94 errors.into_iter().map(Into::into).collect()
95 }
96 } else {
97 lex_errors.into_iter().map(Into::into).collect()
98 };
99
100 if errors.is_empty() {
101 Ok(instance)
102 } else {
103 Err(errors)
104 }
105 }
106}
107
108impl<'a> bluejay_core::executable::ExecutableDocument for ExecutableDocument<'a> {
109 type Value<const CONST: bool> = Value<'a, CONST>;
110 type VariableType = VariableType<'a>;
111 type Argument<const CONST: bool> = Argument<'a, CONST>;
112 type Arguments<const CONST: bool> = Arguments<'a, CONST>;
113 type Directive<const CONST: bool> = Directive<'a, CONST>;
114 type Directives<const CONST: bool> = Directives<'a, CONST>;
115 type FragmentSpread = FragmentSpread<'a>;
116 type Field = Field<'a>;
117 type Selection = Selection<'a>;
118 type SelectionSet = SelectionSet<'a>;
119 type InlineFragment = InlineFragment<'a>;
120 type VariableDefinition = VariableDefinition<'a>;
121 type VariableDefinitions = VariableDefinitions<'a>;
122 type ExplicitOperationDefinition = ExplicitOperationDefinition<'a>;
123 type ImplicitOperationDefinition = ImplicitOperationDefinition<'a>;
124 type OperationDefinition = OperationDefinition<'a>;
125 type FragmentDefinition = FragmentDefinition<'a>;
126 type FragmentDefinitions<'b>
127 = std::slice::Iter<'b, Self::FragmentDefinition>
128 where
129 Self: 'b;
130 type OperationDefinitions<'b>
131 = std::slice::Iter<'b, Self::OperationDefinition>
132 where
133 Self: 'b;
134
135 fn operation_definitions(&self) -> Self::OperationDefinitions<'_> {
136 self.operation_definitions.iter()
137 }
138
139 fn fragment_definitions(&self) -> Self::FragmentDefinitions<'_> {
140 self.fragment_definitions.iter()
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::{ExecutableDocument, Parse};
147 use crate::ast::ParseOptions;
148
149 #[test]
150 fn test_success() {
151 let document = r#"
152 {
153 dog {
154 ...fragmentOne
155 ...fragmentTwo
156 }
157 }
158
159 fragment fragmentOne on Dog {
160 name
161 }
162
163 fragment fragmentTwo on Dog {
164 owner {
165 name
166 }
167 }
168 "#;
169
170 let defs = ExecutableDocument::parse(document).unwrap();
171
172 assert_eq!(2, defs.fragment_definitions().len());
173 assert_eq!(1, defs.operation_definitions().len());
174 }
175
176 #[test]
177 fn test_depth_limit() {
178 let document = r#"query { foo }"#;
182
183 let errors = ExecutableDocument::parse_with_options(
184 document,
185 ParseOptions {
186 graphql_ruby_compatibility: false,
187 max_depth: 2,
188 },
189 )
190 .unwrap_err();
191
192 assert_eq!(1, errors.len(), "{:?}", errors);
193
194 assert_eq!("Max depth exceeded", errors[0].message());
195
196 let executable_document = ExecutableDocument::parse_with_options(
197 document,
198 ParseOptions {
199 graphql_ruby_compatibility: false,
200 max_depth: 3,
201 },
202 )
203 .unwrap();
204 assert_eq!(1, executable_document.operation_definitions().len());
205 }
206}