@@ -6,7 +6,10 @@ use std::{
66    hash:: Hash , 
77} ; 
88
9- use  crate :: utils:: pretty_display:: { get_indent,  PrettyDisplay } ; 
9+ use  crate :: { 
10+     ast:: merge_path:: { Condition ,  MergePath ,  Segment } , 
11+     utils:: pretty_display:: { get_indent,  PrettyDisplay } , 
12+ } ; 
1013
1114use  super :: { arguments:: ArgumentsMap ,  selection_item:: SelectionItem } ; 
1215
@@ -55,10 +58,24 @@ impl Display for SelectionSet {
5558} 
5659
5760impl  SelectionSet  { 
61+     pub  fn  cost ( & self )  -> u64  { 
62+         let  mut  cost = 1 ; 
63+ 
64+         for  node in  & self . items  { 
65+             cost += node. cost ( ) ; 
66+         } 
67+ 
68+         cost
69+     } 
70+ 
5871    pub  fn  is_empty ( & self )  -> bool  { 
5972        self . items . is_empty ( ) 
6073    } 
6174
75+     pub  fn  contains ( & self ,  other :  & Self )  -> bool  { 
76+         selection_items_are_subset_of ( & self . items ,  & other. items ) 
77+     } 
78+ 
6279    pub  fn  variable_usages ( & self )  -> BTreeSet < String >  { 
6380        self . items 
6481            . iter ( ) 
@@ -343,6 +360,234 @@ impl PrettyDisplay for InlineFragmentSelection {
343360    } 
344361} 
345362
363+ pub  fn  selection_items_are_subset_of ( source :  & [ SelectionItem ] ,  target :  & [ SelectionItem ] )  -> bool  { 
364+     target. iter ( ) . all ( |target_node| { 
365+         source
366+             . iter ( ) 
367+             . any ( |source_node| selection_item_is_subset_of ( source_node,  target_node) ) 
368+     } ) 
369+ } 
370+ 
371+ fn  selection_item_is_subset_of ( source :  & SelectionItem ,  target :  & SelectionItem )  -> bool  { 
372+     match  ( source,  target)  { 
373+         ( SelectionItem :: Field ( source_field) ,  SelectionItem :: Field ( target_field) )  => { 
374+             if  source_field. name  != target_field. name  { 
375+                 return  false ; 
376+             } 
377+ 
378+             if  source_field. is_leaf ( )  != target_field. is_leaf ( )  { 
379+                 return  false ; 
380+             } 
381+ 
382+             selection_items_are_subset_of ( 
383+                 & source_field. selections . items , 
384+                 & target_field. selections . items , 
385+             ) 
386+         } 
387+         // TODO: support fragments 
388+         _ => false , 
389+     } 
390+ } 
391+ 
392+ pub  fn  merge_selection_set ( target :  & mut  SelectionSet ,  source :  & SelectionSet ,  as_first :  bool )  { 
393+     if  source. items . is_empty ( )  { 
394+         return ; 
395+     } 
396+ 
397+     let  mut  pending_items = Vec :: with_capacity ( source. items . len ( ) ) ; 
398+     for  source_item in  source. items . iter ( )  { 
399+         let  mut  found = false ; 
400+         for  target_item in  target. items . iter_mut ( )  { 
401+             match  ( source_item,  target_item)  { 
402+                 ( SelectionItem :: Field ( source_field) ,  SelectionItem :: Field ( target_field) )  => { 
403+                     if  source_field == target_field { 
404+                         found = true ; 
405+                         merge_selection_set ( 
406+                             & mut  target_field. selections , 
407+                             & source_field. selections , 
408+                             as_first, 
409+                         ) ; 
410+                         break ; 
411+                     } 
412+                 } 
413+                 ( 
414+                     SelectionItem :: InlineFragment ( source_fragment) , 
415+                     SelectionItem :: InlineFragment ( target_fragment) , 
416+                 )  => { 
417+                     if  source_fragment. type_condition  == target_fragment. type_condition  { 
418+                         found = true ; 
419+                         merge_selection_set ( 
420+                             & mut  target_fragment. selections , 
421+                             & source_fragment. selections , 
422+                             as_first, 
423+                         ) ; 
424+                         break ; 
425+                     } 
426+                 } 
427+                 _ => { } 
428+             } 
429+         } 
430+ 
431+         if  !found { 
432+             pending_items. push ( source_item. clone ( ) ) 
433+         } 
434+     } 
435+ 
436+     if  !pending_items. is_empty ( )  { 
437+         if  as_first { 
438+             let  mut  new_items = pending_items; 
439+             new_items. append ( & mut  target. items ) ; 
440+             target. items  = new_items; 
441+         }  else  { 
442+             target. items . extend ( pending_items) ; 
443+         } 
444+     } 
445+ } 
446+ 
447+ pub  fn  find_selection_set_by_path_mut < ' a > ( 
448+     root_selection_set :  & ' a  mut  SelectionSet , 
449+     path :  & MergePath , 
450+ )  -> Option < & ' a  mut  SelectionSet >  { 
451+     let  mut  current_selection_set = root_selection_set; 
452+ 
453+     for  path_element in  path. inner . iter ( )  { 
454+         match  path_element { 
455+             Segment :: List  => { 
456+                 continue ; 
457+             } 
458+             Segment :: Cast ( type_name,  condition)  => { 
459+                 let  next_selection_set_option =
460+                     current_selection_set
461+                         . items 
462+                         . iter_mut ( ) 
463+                         . find_map ( |item| match  item { 
464+                             SelectionItem :: Field ( _)  => None , 
465+                             SelectionItem :: InlineFragment ( f)  => { 
466+                                 if  f. type_condition . eq ( type_name) 
467+                                     && fragment_condition_equal ( condition,  f) 
468+                                 { 
469+                                     Some ( & mut  f. selections ) 
470+                                 }  else  { 
471+                                     None 
472+                                 } 
473+                             } 
474+                             SelectionItem :: FragmentSpread ( _)  => None , 
475+                         } ) ; 
476+ 
477+                 match  next_selection_set_option { 
478+                     Some ( next_set)  => { 
479+                         current_selection_set = next_set; 
480+                     } 
481+                     None  => { 
482+                         return  None ; 
483+                     } 
484+                 } 
485+             } 
486+             Segment :: Field ( field_name,  args_hash,  condition)  => { 
487+                 let  next_selection_set_option =
488+                     current_selection_set
489+                         . items 
490+                         . iter_mut ( ) 
491+                         . find_map ( |item| match  item { 
492+                             SelectionItem :: Field ( field)  => { 
493+                                 if  field. selection_identifier ( )  == field_name
494+                                     && field. arguments_hash ( )  == * args_hash
495+                                     && field_condition_equal ( condition,  field) 
496+                                 { 
497+                                     Some ( & mut  field. selections ) 
498+                                 }  else  { 
499+                                     None 
500+                                 } 
501+                             } 
502+                             SelectionItem :: InlineFragment ( ..)  => None , 
503+                             SelectionItem :: FragmentSpread ( _)  => None , 
504+                         } ) ; 
505+ 
506+                 match  next_selection_set_option { 
507+                     Some ( next_set)  => { 
508+                         current_selection_set = next_set; 
509+                     } 
510+                     None  => { 
511+                         return  None ; 
512+                     } 
513+                 } 
514+             } 
515+         } 
516+     } 
517+     Some ( current_selection_set) 
518+ } 
519+ 
520+ pub  fn  field_condition_equal ( cond :  & Option < Condition > ,  field :  & FieldSelection )  -> bool  { 
521+     match  cond { 
522+         Some ( cond)  => match  cond { 
523+             Condition :: Include ( var_name)  => { 
524+                 field. include_if . as_ref ( ) . is_some_and ( |v| v == var_name) 
525+             } 
526+             Condition :: Skip ( var_name)  => field. skip_if . as_ref ( ) . is_some_and ( |v| v == var_name) , 
527+         } , 
528+         None  => field. include_if . is_none ( )  && field. skip_if . is_none ( ) , 
529+     } 
530+ } 
531+ 
532+ fn  fragment_condition_equal ( cond :  & Option < Condition > ,  fragment :  & InlineFragmentSelection )  -> bool  { 
533+     match  cond { 
534+         Some ( cond)  => match  cond { 
535+             Condition :: Include ( var_name)  => { 
536+                 fragment. include_if . as_ref ( ) . is_some_and ( |v| v == var_name) 
537+             } 
538+             Condition :: Skip ( var_name)  => fragment. skip_if . as_ref ( ) . is_some_and ( |v| v == var_name) , 
539+         } , 
540+         None  => fragment. include_if . is_none ( )  && fragment. skip_if . is_none ( ) , 
541+     } 
542+ } 
543+ 
544+ /// Find the arguments conflicts between two selections. 
545+ /// Returns a vector of tuples containing the indices of conflicting fields in both "source" and "other" 
546+ /// Both indices are returned in order to allow for easy resolution of conflicts later, in either side. 
547+ pub  fn  find_arguments_conflicts ( 
548+     source :  & SelectionSet , 
549+     other :  & SelectionSet , 
550+ )  -> Vec < ( usize ,  usize ) >  { 
551+     other
552+         . items 
553+         . iter ( ) 
554+         . enumerate ( ) 
555+         . filter_map ( |( index,  other_selection) | { 
556+             if  let  SelectionItem :: Field ( other_field)  = other_selection { 
557+                 let  other_identifier = other_field. selection_identifier ( ) ; 
558+                 let  other_args_hash = other_field. arguments_hash ( ) ; 
559+ 
560+                 let  existing_in_self =
561+                     source
562+                         . items 
563+                         . iter ( ) 
564+                         . enumerate ( ) 
565+                         . find_map ( |( self_index,  self_selection) | { 
566+                             if  let  SelectionItem :: Field ( self_field)  = self_selection { 
567+                                 // If the field selection identifier matches and the arguments hash is different, 
568+                                 // then it means that we can't merge the two input siblings 
569+                                 if  self_field. selection_identifier ( )  == other_identifier
570+                                     && self_field. arguments_hash ( )  != other_args_hash
571+                                 { 
572+                                     return  Some ( self_index) ; 
573+                                 } 
574+                             } 
575+ 
576+                             None 
577+                         } ) ; 
578+ 
579+                 if  let  Some ( existing_index)  = existing_in_self { 
580+                     return  Some ( ( existing_index,  index) ) ; 
581+                 } 
582+ 
583+                 return  None ; 
584+             } 
585+ 
586+             None 
587+         } ) 
588+         . collect ( ) 
589+ } 
590+ 
346591#[ cfg( test) ]  
347592mod  tests { 
348593    use  crate :: ast:: value:: Value ; 
0 commit comments