async_graphql/
look_ahead.rs1use std::collections::HashMap;
2
3use crate::{
4 parser::types::{Field, FragmentDefinition, Selection, SelectionSet},
5 Context, Name, Positioned, SelectionField,
6};
7
8pub struct Lookahead<'a> {
10 fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
11 fields: Vec<&'a Field>,
12 context: &'a Context<'a>,
13}
14
15impl<'a> Lookahead<'a> {
16 pub(crate) fn new(
17 fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
18 field: &'a Field,
19 context: &'a Context<'a>,
20 ) -> Self {
21 Self {
22 fragments,
23 fields: vec![field],
24 context,
25 }
26 }
27
28 #[must_use]
34 pub fn field(&self, name: &str) -> Self {
35 let mut fields = Vec::new();
36 for field in &self.fields {
37 filter(&mut fields, self.fragments, &field.selection_set.node, name)
38 }
39
40 Self {
41 fragments: self.fragments,
42 fields,
43 context: self.context,
44 }
45 }
46
47 #[inline]
49 pub fn exists(&self) -> bool {
50 !self.fields.is_empty()
51 }
52
53 pub fn selection_fields(&self) -> Vec<SelectionField<'a>> {
59 self.fields
60 .iter()
61 .map(|field| SelectionField {
62 fragments: self.fragments,
63 field,
64 context: self.context,
65 })
66 .collect()
67 }
68}
69
70impl<'a> From<SelectionField<'a>> for Lookahead<'a> {
71 fn from(selection_field: SelectionField<'a>) -> Self {
72 Lookahead {
73 fragments: selection_field.fragments,
74 fields: vec![selection_field.field],
75 context: selection_field.context,
76 }
77 }
78}
79
80impl<'a> TryFrom<&[SelectionField<'a>]> for Lookahead<'a> {
86 type Error = ();
87
88 fn try_from(selection_fields: &[SelectionField<'a>]) -> Result<Self, Self::Error> {
89 if selection_fields.is_empty() {
90 Err(())
91 } else {
92 Ok(Lookahead {
93 fragments: selection_fields[0].fragments,
94 fields: selection_fields
95 .iter()
96 .map(|selection_field| selection_field.field)
97 .collect(),
98 context: selection_fields[0].context,
99 })
100 }
101 }
102}
103
104fn filter<'a>(
105 fields: &mut Vec<&'a Field>,
106 fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
107 selection_set: &'a SelectionSet,
108 name: &str,
109) {
110 for item in &selection_set.items {
111 match &item.node {
112 Selection::Field(field) => {
113 if field.node.name.node == name {
114 fields.push(&field.node)
115 }
116 }
117 Selection::InlineFragment(fragment) => {
118 filter(fields, fragments, &fragment.node.selection_set.node, name)
119 }
120 Selection::FragmentSpread(spread) => {
121 if let Some(fragment) = fragments.get(&spread.node.fragment_name.node) {
122 filter(fields, fragments, &fragment.node.selection_set.node, name)
123 }
124 }
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use crate::*;
132
133 #[tokio::test]
134 async fn test_look_ahead() {
135 #[derive(SimpleObject)]
136 #[graphql(internal)]
137 struct Detail {
138 c: i32,
139 d: i32,
140 }
141
142 #[derive(SimpleObject)]
143 #[graphql(internal)]
144 struct MyObj {
145 a: i32,
146 b: i32,
147 detail: Detail,
148 }
149
150 struct Query;
151
152 #[Object(internal)]
153 impl Query {
154 async fn obj(&self, ctx: &Context<'_>, n: i32) -> MyObj {
155 if ctx.look_ahead().field("a").exists() {
156 assert_eq!(n, 1);
158 } else if ctx.look_ahead().field("detail").field("c").exists()
159 && ctx.look_ahead().field("detail").field("d").exists()
160 {
161 assert_eq!(n, 2);
163 } else if ctx.look_ahead().field("detail").field("c").exists() {
164 assert_eq!(n, 3);
166 } else {
167 assert_eq!(n, 4);
169 }
170 MyObj {
171 a: 0,
172 b: 0,
173 detail: Detail { c: 0, d: 0 },
174 }
175 }
176 }
177
178 let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
179
180 assert!(schema
181 .execute(
182 r#"{
183 obj(n: 1) {
184 a
185 }
186 }"#,
187 )
188 .await
189 .is_ok());
190
191 assert!(schema
192 .execute(
193 r#"{
194 obj(n: 1) {
195 k:a
196 }
197 }"#,
198 )
199 .await
200 .is_ok());
201
202 assert!(schema
203 .execute(
204 r#"{
205 obj(n: 3) {
206 detail {
207 c
208 }
209 }
210 }"#,
211 )
212 .await
213 .is_ok());
214
215 assert!(schema
216 .execute(
217 r#"{
218 obj(n: 2) {
219 detail {
220 d
221 }
222
223 detail {
224 c
225 }
226 }
227 }"#,
228 )
229 .await
230 .is_ok());
231
232 assert!(schema
233 .execute(
234 r#"{
235 obj(n: 4) {
236 b
237 }
238 }"#,
239 )
240 .await
241 .is_ok());
242
243 assert!(schema
244 .execute(
245 r#"{
246 obj(n: 1) {
247 ... {
248 a
249 }
250 }
251 }"#,
252 )
253 .await
254 .is_ok());
255
256 assert!(schema
257 .execute(
258 r#"{
259 obj(n: 3) {
260 ... {
261 detail {
262 c
263 }
264 }
265 }
266 }"#,
267 )
268 .await
269 .is_ok());
270
271 assert!(schema
272 .execute(
273 r#"{
274 obj(n: 2) {
275 ... {
276 detail {
277 d
278 }
279
280 detail {
281 c
282 }
283 }
284 }
285 }"#,
286 )
287 .await
288 .is_ok());
289
290 assert!(schema
291 .execute(
292 r#"{
293 obj(n: 1) {
294 ... A
295 }
296 }
297
298 fragment A on MyObj {
299 a
300 }"#,
301 )
302 .await
303 .is_ok());
304
305 assert!(schema
306 .execute(
307 r#"{
308 obj(n: 3) {
309 ... A
310 }
311 }
312
313 fragment A on MyObj {
314 detail {
315 c
316 }
317 }"#,
318 )
319 .await
320 .is_ok());
321
322 assert!(schema
323 .execute(
324 r#"{
325 obj(n: 2) {
326 ... A
327 ... B
328 }
329 }
330
331 fragment A on MyObj {
332 detail {
333 d
334 }
335 }
336
337 fragment B on MyObj {
338 detail {
339 c
340 }
341 }"#,
342 )
343 .await
344 .is_ok());
345 }
346}