graphql_tools/ast/
collect_fields.rs

1use std::collections::HashMap;
2
3use super::{AbstractTypeDefinitionExtension, OperationVisitorContext, SchemaDocumentExtension};
4use crate::ast::ext::{SubTypeExtension, TypeDefinitionExtension};
5use crate::static_graphql::{
6    query::{self, Selection, TypeCondition},
7    schema::{self, TypeDefinition},
8};
9pub fn collect_fields<'a>(
10    selection_set: &query::SelectionSet,
11    parent_type: &schema::TypeDefinition,
12    known_fragments: &HashMap<&str, &query::FragmentDefinition>,
13    context: &'a OperationVisitorContext<'a>,
14) -> HashMap<String, Vec<query::Field>> {
15    let mut map = HashMap::new();
16    let mut visited_fragments_names: Vec<String> = Vec::new();
17
18    collect_fields_inner(
19        selection_set,
20        parent_type,
21        known_fragments,
22        context,
23        &mut map,
24        &mut visited_fragments_names,
25    );
26
27    map
28}
29
30fn does_fragment_condition_match<'a>(
31    fragment_condition: &'a Option<TypeCondition>,
32    current_selection_set_type: &'a TypeDefinition,
33    context: &'a OperationVisitorContext<'a>,
34) -> bool {
35    if let Some(TypeCondition::On(type_name)) = fragment_condition {
36        if let Some(conditional_type) = context.schema.type_by_name(type_name) {
37            if conditional_type
38                .name()
39                .eq(current_selection_set_type.name())
40            {
41                return true;
42            }
43
44            if conditional_type.is_abstract_type() {
45                match conditional_type {
46                    TypeDefinition::Interface(interface_type) => {
47                        return interface_type.is_implemented_by(current_selection_set_type)
48                    }
49                    TypeDefinition::Union(union_type) => {
50                        return union_type.has_sub_type(current_selection_set_type.name())
51                    }
52                    _ => return false,
53                }
54            }
55        }
56
57        false
58    } else {
59        true
60    }
61}
62
63fn collect_fields_inner<'a>(
64    selection_set: &query::SelectionSet,
65    parent_type: &schema::TypeDefinition,
66    known_fragments: &HashMap<&str, &query::FragmentDefinition>,
67    context: &'a OperationVisitorContext<'a>,
68    result_arr: &mut HashMap<String, Vec<query::Field>>,
69    visited_fragments_names: &mut Vec<String>,
70) {
71    selection_set.items.iter().for_each(|item| match item {
72        Selection::Field(f) => {
73            let existing = result_arr.entry(f.name.clone()).or_default();
74            existing.push(f.clone());
75        }
76        Selection::InlineFragment(f) => {
77            if does_fragment_condition_match(&f.type_condition, parent_type, context) {
78                collect_fields_inner(
79                    &f.selection_set,
80                    parent_type,
81                    known_fragments,
82                    context,
83                    result_arr,
84                    visited_fragments_names,
85                );
86            }
87        }
88        Selection::FragmentSpread(f) => {
89            if !visited_fragments_names
90                .iter()
91                .any(|name| f.fragment_name.eq(name))
92            {
93                visited_fragments_names.push(f.fragment_name.clone());
94
95                if let Some(fragment) = known_fragments.get(f.fragment_name.as_str()) {
96                    if does_fragment_condition_match(
97                        &Some(fragment.type_condition.clone()),
98                        parent_type,
99                        context,
100                    ) {
101                        collect_fields_inner(
102                            &fragment.selection_set,
103                            parent_type,
104                            known_fragments,
105                            context,
106                            result_arr,
107                            visited_fragments_names,
108                        );
109                    }
110                }
111            }
112        }
113    });
114}