From 7dcea3c7213632e6ca86321c7429ac228853283c Mon Sep 17 00:00:00 2001 From: Oliver Cooper Date: Fri, 20 Aug 2021 10:49:20 +1200 Subject: [PATCH] Ignore items flagged @skip in SelectionField and Lookahead --- src/context.rs | 19 ++++++++++++++++--- src/look_ahead.rs | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/context.rs b/src/context.rs index 0fded9ff..be7bf5de 100644 --- a/src/context.rs +++ b/src/context.rs @@ -603,7 +603,7 @@ impl<'a> ContextBase<'a, &'a Positioned> { /// } /// ``` pub fn look_ahead(&self) -> Lookahead { - Lookahead::new(&self.query_env.fragments, &self.item.node) + Lookahead::new(&self.query_env.fragments, &self.item.node, self) } /// Get the current field. @@ -653,7 +653,7 @@ impl<'a> ContextBase<'a, &'a Positioned> { pub struct SelectionField<'a> { pub(crate) fragments: &'a HashMap>, pub(crate) field: &'a Field, - context: &'a Context<'a>, + pub(crate) context: &'a Context<'a>, } impl<'a> SelectionField<'a> { @@ -727,7 +727,20 @@ impl<'a> Iterator for SelectionFieldsIter<'a> { fn next(&mut self) -> Option { loop { let it = self.iter.last_mut()?; - match it.next() { + let item = it.next(); + if let Some(item) = item { + // ignore any items that are skipped (i.e. @skip/@include) + if self + .context + .is_skip(&item.node.directives()) + .unwrap_or(false) + { + // TODO: should we throw errors here? they will be caught later in execution and it'd cause major backwards compatibility issues + continue; + } + } + + match item { Some(selection) => match &selection.node { Selection::Field(field) => { return Some(SelectionField { diff --git a/src/look_ahead.rs b/src/look_ahead.rs index 1dab6bf1..68e56637 100644 --- a/src/look_ahead.rs +++ b/src/look_ahead.rs @@ -2,22 +2,26 @@ use std::collections::HashMap; use std::convert::TryFrom; use crate::parser::types::{Field, FragmentDefinition, Selection, SelectionSet}; +use crate::Context; use crate::{Name, Positioned, SelectionField}; /// A selection performed by a query. pub struct Lookahead<'a> { fragments: &'a HashMap>, fields: Vec<&'a Field>, + context: &'a Context<'a>, } impl<'a> Lookahead<'a> { pub(crate) fn new( fragments: &'a HashMap>, field: &'a Field, + context: &'a Context<'a>, ) -> Self { Self { fragments, fields: vec![field], + context, } } @@ -29,12 +33,19 @@ impl<'a> Lookahead<'a> { pub fn field(&self, name: &str) -> Self { let mut fields = Vec::new(); for field in &self.fields { - filter(&mut fields, self.fragments, &field.selection_set.node, name) + filter( + &mut fields, + self.fragments, + &field.selection_set.node, + name, + self.context, + ) } Self { fragments: self.fragments, fields, + context: self.context, } } @@ -50,6 +61,7 @@ impl<'a> From> for Lookahead<'a> { Lookahead { fragments: selection_field.fragments, fields: vec![selection_field.field], + context: selection_field.context, } } } @@ -71,6 +83,7 @@ impl<'a> TryFrom<&[SelectionField<'a>]> for Lookahead<'a> { .iter() .map(|selection_field| selection_field.field) .collect(), + context: selection_fields[0].context, }) } } @@ -81,22 +94,40 @@ fn filter<'a>( fragments: &'a HashMap>, selection_set: &'a SelectionSet, name: &str, + context: &'a Context<'a>, ) { for item in &selection_set.items { // doing this imperatively is a bit nasty, but using iterators would // require a boxed return type (I believe) as its recusive + + // ignore any items that are skipped (i.e. @skip/@include) + if context.is_skip(&item.node.directives()).unwrap_or(false) { + // TODO: should we throw errors here? they will be caught later in execution and it'd cause major backwards compatibility issues + continue; + } + match &item.node { Selection::Field(field) => { if field.node.name.node == name { fields.push(&field.node) } } - Selection::InlineFragment(fragment) => { - filter(fields, fragments, &fragment.node.selection_set.node, name) - } + Selection::InlineFragment(fragment) => filter( + fields, + fragments, + &fragment.node.selection_set.node, + name, + context, + ), Selection::FragmentSpread(spread) => { if let Some(fragment) = fragments.get(&spread.node.fragment_name.node) { - filter(fields, fragments, &fragment.node.selection_set.node, name) + filter( + fields, + fragments, + &fragment.node.selection_set.node, + name, + context, + ) } } }