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