1mod fragments;
5mod operations;
6mod selection;
7mod validation;
8
9pub(crate) use fragments::{fragment_is_recursive, ResolvedFragment};
10pub(crate) use operations::{OperationType, ResolvedOperation};
11pub(crate) use selection::*;
12
13use crate::{
14 constants::TYPENAME_FIELD,
15 normalization::Normalization,
16 schema::{
17 resolve_field_type, EnumId, InputId, ScalarId, Schema, StoredEnum, StoredFieldType,
18 StoredInputType, StoredScalar, TypeId, UnionId,
19 },
20};
21use std::collections::{HashMap, HashSet};
22use thiserror::Error;
23
24#[derive(Debug, Error)]
25#[error("{}", message)]
26pub(crate) struct QueryValidationError {
27 message: String,
28}
29
30impl QueryValidationError {
31 pub(crate) fn new(message: String) -> Self {
32 QueryValidationError { message }
33 }
34}
35
36#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
37pub(crate) struct SelectionId(u32);
38#[derive(Debug, Clone, Copy, PartialEq)]
39pub(crate) struct OperationId(u32);
40
41impl OperationId {
42 pub(crate) fn new(idx: usize) -> Self {
43 OperationId(idx as u32)
44 }
45}
46
47#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
48pub(crate) struct ResolvedFragmentId(u32);
49
50#[derive(Debug, Clone, Copy)]
51pub(crate) struct VariableId(u32);
52
53pub(crate) fn resolve(
54 schema: &Schema,
55 query: &gurkle_parser::query::Document,
56) -> Result<Query, QueryValidationError> {
57 let mut resolved_query: Query = Default::default();
58
59 create_roots(&mut resolved_query, query, schema)?;
60
61 for definition in &query.definitions {
63 match definition {
64 gurkle_parser::query::Definition::Fragment(fragment) => {
65 resolve_fragment(&mut resolved_query, schema, fragment)?
66 }
67 gurkle_parser::query::Definition::Operation(operation) => {
68 resolve_operation(&mut resolved_query, schema, operation)?
69 }
70 }
71 }
72
73 validation::validate_typename_presence(&BoundQuery {
75 query: &resolved_query,
76 schema,
77 })?;
78
79 for (selection_id, _) in resolved_query.selections() {
80 selection::validate_type_conditions(
81 selection_id,
82 &BoundQuery {
83 query: &resolved_query,
84 schema,
85 },
86 )?
87 }
88
89 Ok(resolved_query)
90}
91
92fn create_roots(
93 resolved_query: &mut Query,
94 query: &gurkle_parser::query::Document,
95 schema: &Schema,
96) -> Result<(), QueryValidationError> {
97 for definition in &query.definitions {
99 match definition {
100 gurkle_parser::query::Definition::Fragment(fragment) => {
101 let gurkle_parser::query::TypeCondition::On(on) = &fragment.type_condition;
102 resolved_query.fragments.push(ResolvedFragment {
103 name: fragment.name.clone(),
104 on: schema.find_type(on).ok_or_else(|| {
105 QueryValidationError::new(format!(
106 "Could not find type {} for fragment {} in schema.",
107 on, fragment.name
108 ))
109 })?,
110 selection_set: Vec::new(),
111 });
112 }
113 gurkle_parser::query::Definition::Operation(
114 gurkle_parser::query::OperationDefinition::Mutation(m),
115 ) => {
116 let on = schema.mutation_type().ok_or_else(|| {
117 QueryValidationError::new(
118 "Query contains a mutation operation, but the schema has no mutation type."
119 .to_owned(),
120 )
121 })?;
122 let resolved_operation: ResolvedOperation = ResolvedOperation {
123 object_id: on,
124 query_string: m.to_string(),
125 name: m.name.as_ref().expect("mutation without name").to_owned(),
126 operation_type: operations::OperationType::Mutation,
127 selection_set: Vec::with_capacity(m.selection_set.items.len()),
128 };
129
130 resolved_query.operations.push(resolved_operation);
131 }
132 gurkle_parser::query::Definition::Operation(
133 gurkle_parser::query::OperationDefinition::Query(q),
134 ) => {
135 let on = schema.query_type();
136 let name = match q.name.as_ref() {
137 Some(v) => v,
138 None => {
139 return Err(QueryValidationError::new(
140 "Query was missing an operation name.".to_owned(),
141 ));
142 }
143 };
144
145 let resolved_operation: ResolvedOperation = ResolvedOperation {
146 name: name.to_owned(),
147 query_string: q.to_string(),
148 operation_type: operations::OperationType::Query,
149 object_id: on,
150 selection_set: Vec::with_capacity(q.selection_set.items.len()),
151 };
152
153 resolved_query.operations.push(resolved_operation);
154 }
155 gurkle_parser::query::Definition::Operation(
156 gurkle_parser::query::OperationDefinition::Subscription(s),
157 ) => {
158 let on = schema.subscription_type().ok_or_else(|| {
159 QueryValidationError::new(
160 "Query contains a subscription operation, but the schema has no subscription type.".to_owned()
161 )
162 })?;
163
164 if s.selection_set.items.len() != 1 {
165 return Err(QueryValidationError::new(
166 crate::constants::MULTIPLE_SUBSCRIPTION_FIELDS_ERROR.to_owned(),
167 ));
168 }
169
170 let resolved_operation: ResolvedOperation = ResolvedOperation {
171 name: s
172 .name
173 .as_ref()
174 .expect("subscription without name")
175 .to_owned(),
176 query_string: s.to_string(),
177 operation_type: operations::OperationType::Subscription,
178 object_id: on,
179 selection_set: Vec::with_capacity(s.selection_set.items.len()),
180 };
181
182 resolved_query.operations.push(resolved_operation);
183 }
184 gurkle_parser::query::Definition::Operation(
185 gurkle_parser::query::OperationDefinition::SelectionSet(_),
186 ) => {
187 return Err(QueryValidationError::new(
188 crate::constants::SELECTION_SET_AT_ROOT.to_owned(),
189 ))
190 }
191 }
192 }
193
194 Ok(())
195}
196
197fn resolve_fragment(
198 query: &mut Query,
199 schema: &Schema,
200 fragment_definition: &gurkle_parser::query::FragmentDefinition,
201) -> Result<(), QueryValidationError> {
202 let gurkle_parser::query::TypeCondition::On(on) = &fragment_definition.type_condition;
203 let on = schema.find_type(&on).ok_or_else(|| {
204 QueryValidationError::new(format!(
205 "Could not find type `{}` referenced by fragment `{}`",
206 on, fragment_definition.name
207 ))
208 })?;
209
210 let (id, _) = query
211 .find_fragment(&fragment_definition.name)
212 .ok_or_else(|| {
213 QueryValidationError::new(format!(
214 "Could not find fragment `{}`.",
215 fragment_definition.name
216 ))
217 })?;
218
219 resolve_selection(
220 query,
221 on,
222 &fragment_definition.selection_set,
223 SelectionParent::Fragment(id),
224 schema,
225 )?;
226
227 Ok(())
228}
229
230fn resolve_union_selection(
231 query: &mut Query,
232 _union_id: UnionId,
233 selection_set: &gurkle_parser::query::SelectionSet,
234 parent: SelectionParent,
235 schema: &Schema,
236) -> Result<(), QueryValidationError> {
237 for item in selection_set.items.iter() {
238 match item {
239 gurkle_parser::query::Selection::Field(field) => {
240 if field.name == TYPENAME_FIELD {
241 let id = query.push_selection(Selection::Typename, parent);
242 parent.add_to_selection_set(query, id);
243 } else {
244 return Err(QueryValidationError::new(format!(
245 "Invalid field selection on union field ({:?})",
246 parent
247 )));
248 }
249 }
250 gurkle_parser::query::Selection::InlineFragment(inline_fragment) => {
251 let selection_id = resolve_inline_fragment(query, schema, inline_fragment, parent)?;
252 parent.add_to_selection_set(query, selection_id);
253 }
254 gurkle_parser::query::Selection::FragmentSpread(fragment_spread) => {
255 let (fragment_id, _fragment) = query
256 .find_fragment(&fragment_spread.fragment_name)
257 .ok_or_else(|| {
258 QueryValidationError::new(format!(
259 "Could not find fragment `{}` referenced by fragment spread.",
260 fragment_spread.fragment_name
261 ))
262 })?;
263
264 let id = query.push_selection(Selection::FragmentSpread(fragment_id), parent);
265
266 parent.add_to_selection_set(query, id);
267 }
268 }
269 }
270
271 Ok(())
272}
273
274fn resolve_object_selection<'a>(
275 query: &mut Query,
276 object: &dyn crate::schema::ObjectLike,
277 selection_set: &gurkle_parser::query::SelectionSet,
278 parent: SelectionParent,
279 schema: &'a Schema,
280) -> Result<(), QueryValidationError> {
281 for item in selection_set.items.iter() {
282 match item {
283 gurkle_parser::query::Selection::Field(field) => {
284 if field.name == TYPENAME_FIELD {
285 let id = query.push_selection(Selection::Typename, parent);
286 parent.add_to_selection_set(query, id);
287 continue;
288 }
289
290 let (field_id, schema_field) = object
291 .get_field_by_name(&field.name, schema)
292 .ok_or_else(|| {
293 QueryValidationError::new(format!(
294 "No field named {} on {}",
295 &field.name,
296 object.name()
297 ))
298 })?;
299
300 let id = query.push_selection(
301 Selection::Field(SelectedField {
302 alias: field.alias.clone(),
303 field_id,
304 selection_set: Vec::with_capacity(selection_set.items.len()),
305 }),
306 parent,
307 );
308
309 resolve_selection(
310 query,
311 schema_field.r#type.id,
312 &field.selection_set,
313 SelectionParent::Field(id),
314 schema,
315 )?;
316
317 parent.add_to_selection_set(query, id);
318 }
319 gurkle_parser::query::Selection::InlineFragment(inline) => {
320 let selection_id = resolve_inline_fragment(query, schema, inline, parent)?;
321
322 parent.add_to_selection_set(query, selection_id);
323 }
324 gurkle_parser::query::Selection::FragmentSpread(fragment_spread) => {
325 let (fragment_id, _fragment) = query
326 .find_fragment(&fragment_spread.fragment_name)
327 .ok_or_else(|| {
328 QueryValidationError::new(format!(
329 "Could not find fragment `{}` referenced by fragment spread.",
330 fragment_spread.fragment_name
331 ))
332 })?;
333
334 let id = query.push_selection(Selection::FragmentSpread(fragment_id), parent);
335
336 parent.add_to_selection_set(query, id);
337 }
338 }
339 }
340
341 Ok(())
342}
343
344fn resolve_selection(
345 ctx: &mut Query,
346 on: TypeId,
347 selection_set: &gurkle_parser::query::SelectionSet,
348 parent: SelectionParent,
349 schema: &Schema,
350) -> Result<(), QueryValidationError> {
351 match on {
352 TypeId::Object(oid) => {
353 let object = schema.get_object(oid);
354 resolve_object_selection(ctx, object, selection_set, parent, schema)?;
355 }
356 TypeId::Interface(interface_id) => {
357 let interface = schema.get_interface(interface_id);
358 resolve_object_selection(ctx, interface, selection_set, parent, schema)?;
359 }
360 TypeId::Union(union_id) => {
361 resolve_union_selection(ctx, union_id, selection_set, parent, schema)?;
362 }
363 other => {
364 if !selection_set.items.is_empty() {
365 return Err(QueryValidationError::new(format!(
366 "Selection set on non-object, non-interface type. ({:?})",
367 other
368 )));
369 }
370 }
371 };
372
373 Ok(())
374}
375
376fn resolve_inline_fragment(
377 query: &mut Query,
378 schema: &Schema,
379 inline_fragment: &gurkle_parser::query::InlineFragment,
380 parent: SelectionParent,
381) -> Result<SelectionId, QueryValidationError> {
382 let gurkle_parser::query::TypeCondition::On(on) = inline_fragment
383 .type_condition
384 .as_ref()
385 .expect("missing type condition on inline fragment");
386 let type_id = schema.find_type(on).ok_or_else(|| {
387 QueryValidationError::new(format!(
388 "Could not find type `{}` referenced by inline fragment.",
389 on
390 ))
391 })?;
392
393 let id = query.push_selection(
394 Selection::InlineFragment(InlineFragment {
395 type_id,
396 selection_set: Vec::with_capacity(inline_fragment.selection_set.items.len()),
397 }),
398 parent,
399 );
400
401 resolve_selection(
402 query,
403 type_id,
404 &inline_fragment.selection_set,
405 SelectionParent::InlineFragment(id),
406 schema,
407 )?;
408
409 Ok(id)
410}
411
412fn resolve_operation(
413 query: &mut Query,
414 schema: &Schema,
415 operation: &gurkle_parser::query::OperationDefinition,
416) -> Result<(), QueryValidationError> {
417 match operation {
418 gurkle_parser::query::OperationDefinition::Mutation(m) => {
419 let on = schema.mutation_type().ok_or_else(|| {
420 QueryValidationError::new(
421 "Query contains a mutation operation, but the schema has no mutation type."
422 .to_owned(),
423 )
424 })?;
425 let on = schema.get_object(on);
426
427 let (id, _) = query.find_operation(m.name.as_ref().unwrap()).unwrap();
428
429 resolve_variables(query, &m.variable_definitions, schema, id);
430 resolve_object_selection(
431 query,
432 on,
433 &m.selection_set,
434 SelectionParent::Operation(id),
435 schema,
436 )?;
437 }
438 gurkle_parser::query::OperationDefinition::Query(q) => {
439 let on = schema.get_object(schema.query_type());
440 let (id, _) = query.find_operation(q.name.as_ref().unwrap()).unwrap();
441
442 resolve_variables(query, &q.variable_definitions, schema, id);
443 resolve_object_selection(
444 query,
445 on,
446 &q.selection_set,
447 SelectionParent::Operation(id),
448 schema,
449 )?;
450 }
451 gurkle_parser::query::OperationDefinition::Subscription(s) => {
452 let on = schema.subscription_type().ok_or_else(|| QueryValidationError::new("Query contains a subscription operation, but the schema has no subscription type.".into()))?;
453 let on = schema.get_object(on);
454 let (id, _) = query.find_operation(s.name.as_ref().unwrap()).unwrap();
455
456 resolve_variables(query, &s.variable_definitions, schema, id);
457 resolve_object_selection(
458 query,
459 on,
460 &s.selection_set,
461 SelectionParent::Operation(id),
462 schema,
463 )?;
464 }
465 gurkle_parser::query::OperationDefinition::SelectionSet(_) => {
466 unreachable!("unnamed queries are not supported")
467 }
468 }
469
470 Ok(())
471}
472
473#[derive(Default)]
474pub(crate) struct Query {
475 fragments: Vec<ResolvedFragment>,
476 operations: Vec<ResolvedOperation>,
477 selection_parent_idx: HashMap<SelectionId, SelectionParent>,
478 selections: Vec<Selection>,
479 variables: Vec<ResolvedVariable>,
480}
481
482impl Query {
483 fn push_selection(&mut self, node: Selection, parent: SelectionParent) -> SelectionId {
484 let id = SelectionId(self.selections.len() as u32);
485 self.selections.push(node);
486
487 self.selection_parent_idx.insert(id, parent);
488
489 id
490 }
491
492 pub fn operations(&self) -> impl Iterator<Item = (OperationId, &ResolvedOperation)> {
493 walk_operations(self)
494 }
495
496 pub(crate) fn get_selection(&self, id: SelectionId) -> &Selection {
497 self.selections
498 .get(id.0 as usize)
499 .expect("Query.get_selection")
500 }
501
502 pub(crate) fn get_fragment(&self, id: ResolvedFragmentId) -> &ResolvedFragment {
503 self.fragments
504 .get(id.0 as usize)
505 .expect("Query.get_fragment")
506 }
507
508 pub(crate) fn get_operation(&self, id: OperationId) -> &ResolvedOperation {
509 self.operations
510 .get(id.0 as usize)
511 .expect("Query.get_operation")
512 }
513
514 pub(crate) fn select_operation<'a>(
516 &'a self,
517 name: &str,
518 normalization: Normalization,
519 ) -> Option<(OperationId, &'a ResolvedOperation)> {
520 walk_operations(self).find(|(_id, op)| normalization.operation(&op.name) == name)
521 }
522
523 fn find_fragment(&mut self, name: &str) -> Option<(ResolvedFragmentId, &mut ResolvedFragment)> {
524 self.fragments
525 .iter_mut()
526 .enumerate()
527 .find(|(_, frag)| frag.name == name)
528 .map(|(id, f)| (ResolvedFragmentId(id as u32), f))
529 }
530
531 fn find_operation(&mut self, name: &str) -> Option<(OperationId, &mut ResolvedOperation)> {
532 self.operations
533 .iter_mut()
534 .enumerate()
535 .find(|(_, op)| op.name == name)
536 .map(|(id, op)| (OperationId::new(id), op))
537 }
538
539 fn selections(&self) -> impl Iterator<Item = (SelectionId, &Selection)> {
540 self.selections
541 .iter()
542 .enumerate()
543 .map(|(idx, selection)| (SelectionId(idx as u32), selection))
544 }
545
546 fn walk_selection_set<'a>(
547 &'a self,
548 selection_ids: &'a [SelectionId],
549 ) -> impl Iterator<Item = (SelectionId, &'a Selection)> + 'a {
550 selection_ids
551 .iter()
552 .map(move |id| (*id, self.get_selection(*id)))
553 }
554}
555
556#[derive(Debug)]
557pub(crate) struct ResolvedVariable {
558 pub(crate) operation_id: OperationId,
559 pub(crate) name: String,
560 pub(crate) default: Option<gurkle_parser::query::Value>,
561 pub(crate) r#type: StoredFieldType,
562}
563
564impl ResolvedVariable {
565 pub(crate) fn type_name<'schema>(&self, schema: &'schema Schema) -> &'schema str {
566 self.r#type.id.name(schema)
567 }
568
569 fn collect_used_types(&self, used_types: &mut UsedTypes, schema: &Schema) {
570 match self.r#type.id {
571 TypeId::Input(input_id) => {
572 used_types.types.insert(TypeId::Input(input_id));
573
574 let input = schema.get_input(input_id);
575
576 input.used_input_ids_recursive(used_types, schema)
577 }
578 type_id @ TypeId::Scalar(_) | type_id @ TypeId::Enum(_) => {
579 used_types.types.insert(type_id);
580 }
581 _ => (),
582 }
583 }
584}
585
586#[derive(Debug, Default)]
587pub(crate) struct UsedTypes {
588 pub(crate) types: HashSet<TypeId>,
589 fragments: HashSet<ResolvedFragmentId>,
590}
591
592impl UsedTypes {
593 pub(crate) fn inputs<'s, 'a: 's>(
594 &'s self,
595 schema: &'a Schema,
596 ) -> impl Iterator<Item = (InputId, &'a StoredInputType)> + 's {
597 schema
598 .inputs()
599 .filter(move |(id, _input)| self.types.contains(&TypeId::Input(*id)))
600 }
601
602 pub(crate) fn scalars<'s, 'a: 's>(
603 &'s self,
604 schema: &'a Schema,
605 ) -> impl Iterator<Item = (ScalarId, &'a StoredScalar)> + 's {
606 self.types
607 .iter()
608 .filter_map(TypeId::as_scalar_id)
609 .map(move |scalar_id| (scalar_id, schema.get_scalar(scalar_id)))
610 .filter(|(_id, scalar)| !crate::schema::DEFAULT_SCALARS.contains(&scalar.name.as_str()))
611 }
612
613 pub(crate) fn enums<'a, 'schema: 'a>(
614 &'a self,
615 schema: &'schema Schema,
616 ) -> impl Iterator<Item = (EnumId, &'schema StoredEnum)> + 'a {
617 self.types
618 .iter()
619 .filter_map(TypeId::as_enum_id)
620 .map(move |enum_id| (enum_id, schema.get_enum(enum_id)))
621 }
622
623 pub(crate) fn fragment_ids(&self) -> impl Iterator<Item = ResolvedFragmentId> + '_ {
624 self.fragments.iter().copied()
625 }
626}
627
628fn resolve_variables(
629 query: &mut Query,
630 variables: &[gurkle_parser::query::VariableDefinition],
631 schema: &Schema,
632 operation_id: OperationId,
633) {
634 for var in variables {
635 query.variables.push(ResolvedVariable {
636 operation_id,
637 name: var.name.clone(),
638 default: var.default_value.clone(),
639 r#type: resolve_field_type(schema, &var.var_type),
640 });
641 }
642}
643
644pub(crate) fn walk_operations(
645 query: &Query,
646) -> impl Iterator<Item = (OperationId, &ResolvedOperation)> {
647 query
648 .operations
649 .iter()
650 .enumerate()
651 .map(|(id, op)| (OperationId(id as u32), op))
652}
653
654pub(crate) fn operation_has_no_variables(operation_id: OperationId, query: &Query) -> bool {
655 walk_operation_variables(operation_id, query)
656 .next()
657 .is_none()
658}
659
660pub(crate) fn walk_operation_variables(
661 operation_id: OperationId,
662 query: &Query,
663) -> impl Iterator<Item = (VariableId, &ResolvedVariable)> {
664 query
665 .variables
666 .iter()
667 .enumerate()
668 .map(|(idx, var)| (VariableId(idx as u32), var))
669 .filter(move |(_id, var)| var.operation_id == operation_id)
670}
671
672pub(crate) fn all_used_types(operation_id: OperationId, query: &BoundQuery<'_>) -> UsedTypes {
673 let mut used_types = UsedTypes::default();
674
675 let operation = query.query.get_operation(operation_id);
676
677 for (_id, selection) in query.query.walk_selection_set(&operation.selection_set) {
678 selection.collect_used_types(&mut used_types, query);
679 }
680
681 for (_id, variable) in walk_operation_variables(operation_id, query.query) {
682 variable.collect_used_types(&mut used_types, query.schema);
683 }
684
685 used_types
686}
687
688pub(crate) fn full_path_prefix(selection_id: SelectionId, query: &BoundQuery<'_>) -> String {
689 let mut path = match query.query.get_selection(selection_id) {
690 Selection::FragmentSpread(_) | Selection::InlineFragment(_) => Vec::new(),
691 selection => vec![selection.to_path_segment(query)],
692 };
693
694 let mut item = selection_id;
695
696 while let Some(parent) = query.query.selection_parent_idx.get(&item) {
697 path.push(parent.to_path_segment(query));
698
699 match parent {
700 SelectionParent::Field(id) | SelectionParent::InlineFragment(id) => {
701 item = *id;
702 }
703 _ => break,
704 }
705 }
706
707 path.reverse();
708 path.join("")
709}
710
711#[derive(Clone, Copy)]
712pub(crate) struct BoundQuery<'a> {
713 pub(crate) query: &'a Query,
714 pub(crate) schema: &'a Schema,
715}