async-graphql/src/validation/rules/overlapping_fields_can_be_merged.rs

84 lines
3.2 KiB
Rust
Raw Normal View History

2020-03-22 08:45:59 +00:00
use crate::visitor::{Visitor, VisitorContext};
2020-03-12 09:11:02 +00:00
use graphql_parser::query::{Field, Selection, SelectionSet};
use std::collections::HashMap;
#[derive(Default)]
pub struct OverlappingFieldsCanBeMerged;
impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
fn enter_selection_set(
&mut self,
2020-03-22 08:45:59 +00:00
ctx: &mut VisitorContext<'a>,
2020-03-12 09:11:02 +00:00
selection_set: &'a SelectionSet,
) {
let mut find_conflicts = FindConflicts {
outputs: Default::default(),
ctx,
};
find_conflicts.find(selection_set);
}
}
struct FindConflicts<'a, 'ctx> {
outputs: HashMap<&'a str, &'a Field>,
2020-03-22 08:45:59 +00:00
ctx: &'a mut VisitorContext<'ctx>,
2020-03-12 09:11:02 +00:00
}
impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
pub fn find(&mut self, selection_set: &'a SelectionSet) {
for selection in &selection_set.items {
match selection {
Selection::Field(field) => {
let output_name = field
.alias
2020-03-21 01:32:13 +00:00
.as_deref()
.unwrap_or_else(|| field.name.as_str());
2020-03-12 09:11:02 +00:00
self.add_output(output_name, field);
}
Selection::InlineFragment(inline_fragment) => {
self.find(&inline_fragment.selection_set);
}
Selection::FragmentSpread(fragment_spread) => {
if let Some(fragment) = self.ctx.fragment(&fragment_spread.fragment_name) {
self.find(&fragment.selection_set);
}
}
}
}
}
fn add_output(&mut self, name: &'a str, field: &'a Field) {
if let Some(prev_field) = self.outputs.get(name) {
if prev_field.name != field.name {
self.ctx.report_error(
vec![prev_field.position, field.position],
format!("Fields \"{}\" conflict because \"{}\" and \"{}\" are different fields. Use different aliases on the fields to fetch both if this was intentional.",
name, prev_field.name, field.name));
}
// check arguments
if prev_field.arguments.len() != field.arguments.len() {
self.ctx.report_error(
vec![prev_field.position, field.position],
format!("Fields \"{}\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", name));
}
for (name, value) in &prev_field.arguments {
match field
.arguments
.iter()
.find(|(other_name, _)| other_name == name)
.map(|(_, v)| v)
{
Some(other_value) if value == other_value => {}
_=> self.ctx.report_error(
vec![prev_field.position, field.position],
format!("Fields \"{}\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", name)),
}
}
} else {
self.outputs.insert(name, field);
}
}
}