Ignore items flagged @skip in SelectionField and Lookahead

This commit is contained in:
Oliver Cooper 2021-08-20 10:49:20 +12:00
parent f2e7c1a80c
commit 7dcea3c721
2 changed files with 52 additions and 8 deletions

View File

@ -603,7 +603,7 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
/// }
/// ```
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<Field>> {
pub struct SelectionField<'a> {
pub(crate) fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
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<Self::Item> {
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 {

View File

@ -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<Name, Positioned<FragmentDefinition>>,
fields: Vec<&'a Field>,
context: &'a Context<'a>,
}
impl<'a> Lookahead<'a> {
pub(crate) fn new(
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
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<SelectionField<'a>> 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<Name, Positioned<FragmentDefinition>>,
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,
)
}
}
}