1extern crate crypto;
2extern crate graphql_parser;
3extern crate itertools;
4
5pub mod error;
6pub mod traverse;
7pub mod visitor;
8
9mod wrappers;
10
11use crypto::digest::Digest;
12use crypto::sha2::Sha256;
13use error::*;
14use graphql_parser::query::*;
15use itertools::FoldWhile::{Continue, Done};
16use itertools::Itertools;
17use std::collections::BTreeSet;
18use std::result::Result::*;
19use traverse::*;
20use visitor::Visitor;
21use wrappers::FragmentDefinitionContainer;
22
23struct FragmentSpreadVisitor<'a> {
24 document: &'a Document,
26
27 used_fragments: Vec<&'a FragmentDefinition>,
29
30 visited_fragments: Vec<&'a FragmentDefinition>,
33}
34
35impl<'a> Visitor for FragmentSpreadVisitor<'a> {
36 fn visit_fragment_spread_enter(
37 &mut self,
38 fragment_spread: &FragmentSpread,
39 ) -> Result<(), GraphQLError> {
40 let fragment = self
41 .document
42 .definitions
43 .iter()
44 .filter_map(|definition| filter_fragment_defintion(definition))
45 .find(|fragment_definition| fragment_definition.name == fragment_spread.fragment_name)
46 .ok_or(GraphQLError::FragmentNotFound)?;
47
48 let found = &self
49 .visited_fragments
50 .iter()
51 .find(|visited| &visited.name == &fragment.name);
52
53 if let Some(fragment) = found {
54 return Err(GraphQLError::InfiniteFragmentRecursionError(
55 fragment.name.clone(),
56 ));
57 }
58
59 let mut used_fragments: BTreeSet<_> = self
60 .used_fragments
61 .iter()
62 .map(|fragment| FragmentDefinitionContainer(fragment))
63 .collect();
64 used_fragments.insert(FragmentDefinitionContainer(fragment));
65
66 let used_fragments = used_fragments
67 .iter()
68 .map(|container| container.0)
69 .collect::<Vec<_>>();
70
71 let mut sub_visitor = FragmentSpreadVisitor {
72 document: &self.document,
73 visited_fragments: [&self.visited_fragments[..], &[fragment]].concat(),
74 used_fragments: used_fragments.clone(),
75 };
76
77 self.used_fragments = used_fragments;
78
79 let mut traversal = Traversal {
80 visitor: &mut sub_visitor,
81 };
82
83 traversal.handle_fragment_definition(&fragment)?;
84
85 Ok(())
86 }
87}
88
89pub fn generate_operation_id(query: &str, operation_name: &str) -> Result<String, GraphQLError> {
103 let mut hasher = Sha256::new();
104 let operation = select_operation(query, operation_name)?;
105 hasher.input_str(operation.as_str());
106
107 Ok(hasher.result_str())
108}
109
110pub fn generate_default_operation_id(query: &str) -> Result<String, GraphQLError> {
123 let operation_name = get_default_operation_name(query)?;
124
125 generate_operation_id(query, operation_name.as_str())
126}
127
128pub fn get_default_operation_name(query: &str) -> Result<String, GraphQLError> {
148 let document = parse_query(query)?;
149
150 document
151 .definitions
152 .iter()
153 .filter_map(|definition| filter_operation_definition(definition))
154 .fold_while(
155 Err(GraphQLError::OperationNotFound),
156 |result, operation_definition| match operation_definition {
157 OperationDefinition::Query(query) => {
158 if result.is_ok() {
159 Done(Err(GraphQLError::MultipleOperation))
160 } else {
161 Continue(query.clone().name.ok_or(GraphQLError::AnonymousOperation))
162 }
163 }
164 OperationDefinition::Mutation(mutation) => {
165 if result.is_ok() {
166 Done(Err(GraphQLError::MultipleOperation))
167 } else {
168 Continue(
169 mutation
170 .clone()
171 .name
172 .ok_or(GraphQLError::AnonymousOperation),
173 )
174 }
175 }
176 OperationDefinition::SelectionSet(_) => Done(Err(GraphQLError::AnonymousOperation)),
177 OperationDefinition::Subscription(_) => Done(Err(GraphQLError::AnonymousOperation)),
179 },
180 )
181 .into_inner()
182}
183
184pub fn select_operation(query: &str, operation_name: &str) -> Result<String, GraphQLError> {
197 let document = parse_query(query)?;
198 let operation = select_operation_definition(&document, &operation_name)
199 .ok_or(GraphQLError::OperationNotFound)?;
200
201 let mut visitor = FragmentSpreadVisitor {
202 document: &document,
203 used_fragments: vec![],
204 visited_fragments: vec![],
205 };
206
207 {
209 let mut traversal = Traversal {
210 visitor: &mut visitor,
211 };
212 traversal.handle_operation_definition(&operation)?;
213 }
214
215 let fragments = visitor
216 .used_fragments
217 .iter()
218 .map(|fragment| format!("{}", fragment))
219 .collect::<Vec<String>>()
220 .join("\n");
221
222 Ok(format!("{}{}", operation, fragments))
223}
224
225fn select_operation_definition<'a>(
226 document: &'a Document,
227 operation_name: &'a str,
228) -> Option<&'a OperationDefinition> {
229 document
230 .definitions
231 .iter()
232 .filter_map(|definition| filter_operation_definition(definition))
233 .find(|operation_definition| match operation_definition {
234 OperationDefinition::Query(query) => is_operation_name(&query.name, &operation_name),
235 OperationDefinition::Mutation(mutation) => {
236 is_operation_name(&mutation.name, &operation_name)
237 }
238 _ => false,
239 })
240}
241
242fn filter_operation_definition(definition: &Definition) -> Option<&OperationDefinition> {
243 match definition {
244 Definition::Operation(operation_definition) => Some(&operation_definition),
245 _ => None,
246 }
247}
248
249fn filter_fragment_defintion(definition: &Definition) -> Option<&FragmentDefinition> {
250 match definition {
251 Definition::Fragment(fragment_definition) => Some(&fragment_definition),
252 _ => None,
253 }
254}
255
256fn is_operation_name(name: &Option<String>, operation_name: &str) -> bool {
257 name.as_ref().map_or(false, |name| name == operation_name)
258}