graphql_tools/ast/
collect_fields.rs1use 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}