2022-01-28 01:46:14 +00:00
use std ::collections ::{ HashMap , HashSet } ;
2020-10-15 06:38:10 +00:00
2020-09-06 05:38:31 +00:00
use crate ::parser ::types ::{ Field , Selection , SelectionSet } ;
2020-03-24 10:54:22 +00:00
use crate ::validation ::visitor ::{ Visitor , VisitorContext } ;
2020-05-10 02:59:51 +00:00
use crate ::Positioned ;
2020-03-12 09:11:02 +00:00
#[ 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-10 02:59:51 +00:00
selection_set : & ' a Positioned < SelectionSet > ,
2020-03-12 09:11:02 +00:00
) {
let mut find_conflicts = FindConflicts {
outputs : Default ::default ( ) ,
2022-01-28 01:46:14 +00:00
visited : Default ::default ( ) ,
2020-03-12 09:11:02 +00:00
ctx ,
} ;
find_conflicts . find ( selection_set ) ;
}
}
struct FindConflicts < ' a , ' ctx > {
2020-05-10 02:59:51 +00:00
outputs : HashMap < & ' a str , & ' a Positioned < Field > > ,
2022-01-28 01:46:14 +00:00
visited : HashSet < & ' a str > ,
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-10 02:59:51 +00:00
pub fn find ( & mut self , selection_set : & ' a Positioned < SelectionSet > ) {
2020-09-06 05:38:31 +00:00
for selection in & selection_set . node . 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
2020-09-06 05:38:31 +00:00
. node
2020-03-12 09:11:02 +00:00
. alias
2020-05-09 09:55:04 +00:00
. as_ref ( )
2020-09-06 05:38:31 +00:00
. map ( | name | & name . node )
. unwrap_or_else ( | | & field . node . name . node ) ;
2020-05-16 13:14:26 +00:00
self . add_output ( & output_name , field ) ;
2020-03-12 09:11:02 +00:00
}
Selection ::InlineFragment ( inline_fragment ) = > {
2020-09-06 05:38:31 +00:00
self . find ( & inline_fragment . node . selection_set ) ;
2020-03-12 09:11:02 +00:00
}
Selection ::FragmentSpread ( fragment_spread ) = > {
2020-09-06 06:16:36 +00:00
if let Some ( fragment ) =
self . ctx . fragment ( & fragment_spread . node . fragment_name . node )
{
2022-01-28 01:46:14 +00:00
if ! self
. visited
. insert ( fragment_spread . node . fragment_name . node . as_str ( ) )
{
// To avoid recursing itself, this error is detected by the `NoFragmentCycles` validator.
continue ;
}
2020-09-06 05:38:31 +00:00
self . find ( & fragment . node . selection_set ) ;
2020-03-12 09:11:02 +00:00
}
}
}
}
}
2020-05-10 02:59:51 +00:00
fn add_output ( & mut self , name : & ' a str , field : & ' a Positioned < Field > ) {
2020-03-12 09:11:02 +00:00
if let Some ( prev_field ) = self . outputs . get ( name ) {
2020-09-06 05:38:31 +00:00
if prev_field . node . name . node ! = field . node . name . node {
2020-03-12 09:11:02 +00:00
self . ctx . report_error (
2020-09-06 05:38:31 +00:00
vec! [ prev_field . pos , field . pos ] ,
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. " ,
2020-09-06 05:38:31 +00:00
name , prev_field . node . name . node , field . node . name . node ) ) ;
2020-03-12 09:11:02 +00:00
}
// check arguments
2020-09-06 05:38:31 +00:00
if prev_field . node . arguments . len ( ) ! = field . node . arguments . len ( ) {
2020-03-12 09:11:02 +00:00
self . ctx . report_error (
2020-09-06 05:38:31 +00:00
vec! [ prev_field . pos , field . pos ] ,
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 ) ) ;
}
2020-09-06 05:38:31 +00:00
for ( name , value ) in & prev_field . node . arguments {
match field . node . get_argument ( & name . node ) {
2020-03-12 09:11:02 +00:00
Some ( other_value ) if value = = other_value = > { }
_ = > self . ctx . report_error (
2020-09-06 05:38:31 +00:00
vec! [ prev_field . pos , field . pos ] ,
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 ) ;
}
}
}