2020-05-09 09:55:04 +00:00
use crate ::parser ::ast ::{ Field , Selection , SelectionSet } ;
2020-03-24 10:54:22 +00:00
use crate ::validation ::visitor ::{ Visitor , VisitorContext } ;
2020-05-09 09:55:04 +00:00
use crate ::Spanned ;
2020-03-12 09:11:02 +00:00
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-05-09 09:55:04 +00:00
selection_set : & ' a Spanned < SelectionSet > ,
2020-03-12 09:11:02 +00:00
) {
let mut find_conflicts = FindConflicts {
outputs : Default ::default ( ) ,
ctx ,
} ;
find_conflicts . find ( selection_set ) ;
}
}
struct FindConflicts < ' a , ' ctx > {
2020-05-09 09:55:04 +00:00
outputs : HashMap < & ' a str , & ' a Spanned < 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 > {
2020-05-09 09:55:04 +00:00
pub fn find ( & mut self , selection_set : & ' a Spanned < SelectionSet > ) {
2020-03-12 09:11:02 +00:00
for selection in & selection_set . items {
2020-05-09 09:55:04 +00:00
match & selection . node {
2020-03-12 09:11:02 +00:00
Selection ::Field ( field ) = > {
let output_name = field
. alias
2020-05-09 09:55:04 +00:00
. as_ref ( )
. map ( | name | name . as_str ( ) )
2020-03-21 01:32:13 +00:00
. 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 ) ;
}
}
}
}
}
2020-05-09 09:55:04 +00:00
fn add_output ( & mut self , name : & ' a str , field : & ' a Spanned < Field > ) {
2020-03-12 09:11:02 +00:00
if let Some ( prev_field ) = self . outputs . get ( name ) {
if prev_field . name ! = field . name {
self . ctx . report_error (
2020-05-09 09:55:04 +00:00
vec! [ prev_field . position ( ) , field . position ( ) ] ,
2020-03-12 09:11:02 +00:00
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 (
2020-05-09 09:55:04 +00:00
vec! [ prev_field . position ( ) , field . position ( ) ] ,
2020-03-12 09:11:02 +00:00
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 {
2020-05-09 09:55:04 +00:00
match field . get_argument ( name . as_str ( ) )
2020-03-12 09:11:02 +00:00
{
Some ( other_value ) if value = = other_value = > { }
_ = > self . ctx . report_error (
2020-05-09 09:55:04 +00:00
vec! [ prev_field . position ( ) , field . position ( ) ] ,
2020-03-12 09:11:02 +00:00
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 ) ;
}
}
}