1use super::spec::{MutationSpec, MutationTargetSymbol};
19use std::collections::HashMap;
20
21pub fn target_conflicts(a: &MutationTargetSymbol, b: &MutationTargetSymbol) -> bool {
60 match (a, b) {
61 (MutationTargetSymbol::ById(id_a), MutationTargetSymbol::ById(id_b)) => id_a == id_b,
63
64 (MutationTargetSymbol::ByPath(path_a), MutationTargetSymbol::ByPath(path_b)) => {
66 path_a == path_b
67 }
68
69 (
71 MutationTargetSymbol::ByKindAndName(kind_a, name_a),
72 MutationTargetSymbol::ByKindAndName(kind_b, name_b),
73 ) => kind_a == kind_b && name_a == name_b,
74
75 (
77 MutationTargetSymbol::ByAffectedId {
78 parent_id: parent_a,
79 name: name_a,
80 ..
81 },
82 MutationTargetSymbol::ByAffectedId {
83 parent_id: parent_b,
84 name: name_b,
85 ..
86 },
87 ) => parent_a == parent_b && name_a == name_b,
88
89 _ => true,
92 }
93}
94
95pub fn specs_conflict(a: &MutationSpec, b: &MutationSpec) -> bool {
136 use MutationSpec::*;
137
138 match (a, b) {
139 (
142 AddField {
143 target: target_a,
144 field_name: field_a,
145 ..
146 },
147 AddField {
148 target: target_b,
149 field_name: field_b,
150 ..
151 },
152 ) => target_conflicts(target_a, target_b) && field_a == field_b,
153 (
155 AddField {
156 target: target_a,
157 field_name: field_a,
158 ..
159 },
160 RemoveField {
161 target: target_b,
162 field_name: field_b,
163 ..
164 },
165 )
166 | (
167 RemoveField {
168 target: target_a,
169 field_name: field_a,
170 ..
171 },
172 AddField {
173 target: target_b,
174 field_name: field_b,
175 ..
176 },
177 )
178 | (
179 RemoveField {
180 target: target_a,
181 field_name: field_a,
182 ..
183 },
184 RemoveField {
185 target: target_b,
186 field_name: field_b,
187 ..
188 },
189 ) => target_conflicts(target_a, target_b) && field_a == field_b,
190
191 (
194 AddVariant {
195 target: target_a,
196 variant_name: variant_a,
197 ..
198 },
199 AddVariant {
200 target: target_b,
201 variant_name: variant_b,
202 ..
203 },
204 ) => target_conflicts(target_a, target_b) && variant_a == variant_b,
205 (
206 AddVariant {
207 target: target_a,
208 variant_name: variant_a,
209 ..
210 },
211 RemoveVariant {
212 target: target_b,
213 variant_name: variant_b,
214 ..
215 },
216 )
217 | (
218 RemoveVariant {
219 target: target_a,
220 variant_name: variant_a,
221 ..
222 },
223 AddVariant {
224 target: target_b,
225 variant_name: variant_b,
226 ..
227 },
228 )
229 | (
230 RemoveVariant {
231 target: target_a,
232 variant_name: variant_a,
233 ..
234 },
235 RemoveVariant {
236 target: target_b,
237 variant_name: variant_b,
238 ..
239 },
240 ) => target_conflicts(target_a, target_b) && variant_a == variant_b,
241
242 (
245 AddMethod {
246 target: target_a,
247 method_name: method_a,
248 ..
249 },
250 AddMethod {
251 target: target_b,
252 method_name: method_b,
253 ..
254 },
255 ) => target_conflicts(target_a, target_b) && method_a == method_b,
256 (
257 AddMethod {
258 target: target_a,
259 method_name: method_a,
260 ..
261 },
262 RemoveMethod {
263 target: target_b,
264 method_name: method_b,
265 ..
266 },
267 )
268 | (
269 RemoveMethod {
270 target: target_a,
271 method_name: method_a,
272 ..
273 },
274 AddMethod {
275 target: target_b,
276 method_name: method_b,
277 ..
278 },
279 )
280 | (
281 RemoveMethod {
282 target: target_a,
283 method_name: method_a,
284 ..
285 },
286 RemoveMethod {
287 target: target_b,
288 method_name: method_b,
289 ..
290 },
291 ) => target_conflicts(target_a, target_b) && method_a == method_b,
292
293 (
295 RemoveMod {
296 target: target_a, ..
297 },
298 RemoveMod {
299 target: target_b, ..
300 },
301 )
302 | (
303 RemoveMod {
304 target: target_a, ..
305 },
306 CreateMod {
307 target: target_b, ..
308 },
309 )
310 | (
311 CreateMod {
312 target: target_a, ..
313 },
314 RemoveMod {
315 target: target_b, ..
316 },
317 )
318 | (
319 CreateMod {
320 target: target_a, ..
321 },
322 CreateMod {
323 target: target_b, ..
324 },
325 ) => target_conflicts(target_a, target_b),
326
327 (
329 Rename {
330 target: target_a, ..
331 },
332 Rename {
333 target: target_b, ..
334 },
335 ) => target_conflicts(target_a, target_b),
336
337 (
339 AddDerive {
340 target: target_a, ..
341 },
342 AddDerive {
343 target: target_b, ..
344 },
345 )
346 | (
347 AddDerive {
348 target: target_a, ..
349 },
350 RemoveDerive {
351 target: target_b, ..
352 },
353 )
354 | (
355 RemoveDerive {
356 target: target_a, ..
357 },
358 AddDerive {
359 target: target_b, ..
360 },
361 )
362 | (
363 RemoveDerive {
364 target: target_a, ..
365 },
366 RemoveDerive {
367 target: target_b, ..
368 },
369 ) => target_conflicts(target_a, target_b),
370
371 (
373 ChangeVisibility {
374 target: target_a, ..
375 },
376 ChangeVisibility {
377 target: target_b, ..
378 },
379 ) => target_conflicts(target_a, target_b),
380
381 (
383 RemoveItem {
384 target: target_a, ..
385 },
386 RemoveItem {
387 target: target_b, ..
388 },
389 ) => target_conflicts(target_a, target_b),
390
391 (
393 ExtractTrait {
394 target: target_a, ..
395 },
396 ExtractTrait {
397 target: target_b, ..
398 },
399 )
400 | (
401 ExtractTrait {
402 target: target_a, ..
403 },
404 InlineTrait {
405 target: target_b, ..
406 },
407 )
408 | (
409 InlineTrait {
410 target: target_a, ..
411 },
412 ExtractTrait {
413 target: target_b, ..
414 },
415 )
416 | (
417 InlineTrait {
418 target: target_a, ..
419 },
420 InlineTrait {
421 target: target_b, ..
422 },
423 ) => target_conflicts(target_a, target_b),
424
425 (
427 EnumToTrait {
428 target: target_a, ..
429 },
430 EnumToTrait {
431 target: target_b, ..
432 },
433 ) => target_conflicts(target_a, target_b),
434
435 (
437 DuplicateFunction {
438 target: target_a, ..
439 },
440 DuplicateFunction {
441 target: target_b, ..
442 },
443 )
444 | (
445 DuplicateStruct {
446 target: target_a, ..
447 },
448 DuplicateStruct {
449 target: target_b, ..
450 },
451 )
452 | (
453 DuplicateEnum {
454 target: target_a, ..
455 },
456 DuplicateEnum {
457 target: target_b, ..
458 },
459 )
460 | (
461 DuplicateModTree {
462 target: target_a, ..
463 },
464 DuplicateModTree {
465 target: target_b, ..
466 },
467 ) => target_conflicts(target_a, target_b),
468
469 (
471 MoveItem {
472 source: source_a,
473 target: target_a,
474 ..
475 },
476 MoveItem {
477 source: source_b,
478 target: target_b,
479 ..
480 },
481 ) => {
482 target_conflicts(source_a, source_b) || target_conflicts(target_a, target_b)
484 }
485
486 (
488 AddStructLiteralField {
489 target: target_a, ..
490 },
491 AddStructLiteralField {
492 target: target_b, ..
493 },
494 )
495 | (
496 AddStructLiteralField {
497 target: target_a, ..
498 },
499 RemoveStructLiteralField {
500 target: target_b, ..
501 },
502 )
503 | (
504 RemoveStructLiteralField {
505 target: target_a, ..
506 },
507 AddStructLiteralField {
508 target: target_b, ..
509 },
510 )
511 | (
512 RemoveStructLiteralField {
513 target: target_a, ..
514 },
515 RemoveStructLiteralField {
516 target: target_b, ..
517 },
518 ) => target_conflicts(target_a, target_b),
519
520 (
524 AddField {
525 target: target_a, ..
526 },
527 RemoveItem {
528 target: target_b, ..
529 },
530 )
531 | (
532 RemoveItem {
533 target: target_a, ..
534 },
535 AddField {
536 target: target_b, ..
537 },
538 )
539 | (
540 AddMethod {
541 target: target_a, ..
542 },
543 RemoveItem {
544 target: target_b, ..
545 },
546 )
547 | (
548 RemoveItem {
549 target: target_a, ..
550 },
551 AddMethod {
552 target: target_b, ..
553 },
554 )
555 | (
556 AddVariant {
557 target: target_a, ..
558 },
559 RemoveItem {
560 target: target_b, ..
561 },
562 )
563 | (
564 RemoveItem {
565 target: target_a, ..
566 },
567 AddVariant {
568 target: target_b, ..
569 },
570 )
571 | (
572 AddDerive {
573 target: target_a, ..
574 },
575 RemoveItem {
576 target: target_b, ..
577 },
578 )
579 | (
580 RemoveItem {
581 target: target_a, ..
582 },
583 AddDerive {
584 target: target_b, ..
585 },
586 )
587 | (
588 ChangeVisibility {
589 target: target_a, ..
590 },
591 RemoveItem {
592 target: target_b, ..
593 },
594 )
595 | (
596 RemoveItem {
597 target: target_a, ..
598 },
599 ChangeVisibility {
600 target: target_b, ..
601 },
602 )
603 | (
604 Rename {
605 target: target_a, ..
606 },
607 RemoveItem {
608 target: target_b, ..
609 },
610 )
611 | (
612 RemoveItem {
613 target: target_a, ..
614 },
615 Rename {
616 target: target_b, ..
617 },
618 ) => target_conflicts(target_a, target_b),
619
620 (AddItem { .. }, AddItem { .. }) => true, (OrganizeImports { .. }, OrganizeImports { .. }) => true,
624
625 (LoopToIterator { module_id: m_a, .. }, LoopToIterator { module_id: m_b, .. })
629 | (UnwrapToQuestion { module_id: m_a, .. }, UnwrapToQuestion { module_id: m_b, .. })
630 | (AssignOp { module_id: m_a, .. }, AssignOp { module_id: m_b, .. })
631 | (BoolSimplify { module_id: m_a, .. }, BoolSimplify { module_id: m_b, .. })
632 | (CloneOnCopy { module_id: m_a, .. }, CloneOnCopy { module_id: m_b, .. })
633 | (CollapsibleIf { module_id: m_a, .. }, CollapsibleIf { module_id: m_b, .. })
634 | (NoOpArmToTodo { module_id: m_a, .. }, NoOpArmToTodo { module_id: m_b, .. })
635 | (ComparisonToMethod { module_id: m_a, .. }, ComparisonToMethod { module_id: m_b, .. })
636 | (RedundantClosure { module_id: m_a, .. }, RedundantClosure { module_id: m_b, .. })
637 | (IntroduceVariable { module_id: m_a, .. }, IntroduceVariable { module_id: m_b, .. })
638 | (ManualMap { module_id: m_a, .. }, ManualMap { module_id: m_b, .. })
639 | (MatchToIfLet { module_id: m_a, .. }, MatchToIfLet { module_id: m_b, .. })
640 | (FilterNext { module_id: m_a, .. }, FilterNext { module_id: m_b, .. })
641 | (MapUnwrapOr { module_id: m_a, .. }, MapUnwrapOr { module_id: m_b, .. }) => m_a == m_b,
642
643 (
646 ReplaceExpr {
647 fn_id: f_a,
648 module_id: m_a,
649 ..
650 },
651 ReplaceExpr {
652 fn_id: f_b,
653 module_id: m_b,
654 ..
655 },
656 )
657 | (
658 RemoveStatement {
659 fn_id: f_a,
660 module_id: m_a,
661 ..
662 },
663 RemoveStatement {
664 fn_id: f_b,
665 module_id: m_b,
666 ..
667 },
668 )
669 | (
670 ReplaceStatement {
671 fn_id: f_a,
672 module_id: m_a,
673 ..
674 },
675 ReplaceStatement {
676 fn_id: f_b,
677 module_id: m_b,
678 ..
679 },
680 ) => {
681 (f_a.is_some() && f_a == f_b) || (f_a.is_none() && f_b.is_none() && m_a == m_b)
683 }
684
685 (InsertStatement { fn_id: f_a, .. }, InsertStatement { fn_id: f_b, .. }) => f_a == f_b,
687
688 (AddMatchArm { target: t_a, .. }, AddMatchArm { target: t_b, .. })
690 | (RemoveMatchArm { target: t_a, .. }, RemoveMatchArm { target: t_b, .. })
691 | (ReplaceMatchArm { target: t_a, .. }, ReplaceMatchArm { target: t_b, .. }) => t_a == t_b,
692
693 (ReplaceType { .. }, ReplaceType { .. }) => true, (AddSpec { .. }, AddSpec { .. }) => true, (PluginTransform { .. }, PluginTransform { .. }) => true, (
705 AddField {
706 target: target_a, ..
707 },
708 AddDerive {
709 target: target_b, ..
710 },
711 )
712 | (
713 AddField {
714 target: target_a, ..
715 },
716 RemoveDerive {
717 target: target_b, ..
718 },
719 )
720 | (
721 AddField {
722 target: target_a, ..
723 },
724 ChangeVisibility {
725 target: target_b, ..
726 },
727 )
728 | (
729 AddField {
730 target: target_a, ..
731 },
732 Rename {
733 target: target_b, ..
734 },
735 )
736 | (
737 RemoveField {
738 target: target_a, ..
739 },
740 AddDerive {
741 target: target_b, ..
742 },
743 )
744 | (
745 RemoveField {
746 target: target_a, ..
747 },
748 RemoveDerive {
749 target: target_b, ..
750 },
751 )
752 | (
753 RemoveField {
754 target: target_a, ..
755 },
756 ChangeVisibility {
757 target: target_b, ..
758 },
759 )
760 | (
761 RemoveField {
762 target: target_a, ..
763 },
764 Rename {
765 target: target_b, ..
766 },
767 )
768 | (
769 AddVariant {
770 target: target_a, ..
771 },
772 AddDerive {
773 target: target_b, ..
774 },
775 )
776 | (
777 AddVariant {
778 target: target_a, ..
779 },
780 RemoveDerive {
781 target: target_b, ..
782 },
783 )
784 | (
785 AddVariant {
786 target: target_a, ..
787 },
788 ChangeVisibility {
789 target: target_b, ..
790 },
791 )
792 | (
793 AddVariant {
794 target: target_a, ..
795 },
796 Rename {
797 target: target_b, ..
798 },
799 )
800 | (
801 RemoveVariant {
802 target: target_a, ..
803 },
804 AddDerive {
805 target: target_b, ..
806 },
807 )
808 | (
809 RemoveVariant {
810 target: target_a, ..
811 },
812 RemoveDerive {
813 target: target_b, ..
814 },
815 )
816 | (
817 RemoveVariant {
818 target: target_a, ..
819 },
820 ChangeVisibility {
821 target: target_b, ..
822 },
823 )
824 | (
825 RemoveVariant {
826 target: target_a, ..
827 },
828 Rename {
829 target: target_b, ..
830 },
831 )
832 | (
833 AddMethod {
834 target: target_a, ..
835 },
836 AddDerive {
837 target: target_b, ..
838 },
839 )
840 | (
841 AddMethod {
842 target: target_a, ..
843 },
844 RemoveDerive {
845 target: target_b, ..
846 },
847 )
848 | (
849 AddMethod {
850 target: target_a, ..
851 },
852 ChangeVisibility {
853 target: target_b, ..
854 },
855 )
856 | (
857 AddMethod {
858 target: target_a, ..
859 },
860 Rename {
861 target: target_b, ..
862 },
863 )
864 | (
865 RemoveMethod {
866 target: target_a, ..
867 },
868 AddDerive {
869 target: target_b, ..
870 },
871 )
872 | (
873 RemoveMethod {
874 target: target_a, ..
875 },
876 RemoveDerive {
877 target: target_b, ..
878 },
879 )
880 | (
881 RemoveMethod {
882 target: target_a, ..
883 },
884 ChangeVisibility {
885 target: target_b, ..
886 },
887 )
888 | (
889 RemoveMethod {
890 target: target_a, ..
891 },
892 Rename {
893 target: target_b, ..
894 },
895 )
896 | (
897 AddDerive {
898 target: target_a, ..
899 },
900 ChangeVisibility {
901 target: target_b, ..
902 },
903 )
904 | (
905 AddDerive {
906 target: target_a, ..
907 },
908 Rename {
909 target: target_b, ..
910 },
911 )
912 | (
913 RemoveDerive {
914 target: target_a, ..
915 },
916 ChangeVisibility {
917 target: target_b, ..
918 },
919 )
920 | (
921 RemoveDerive {
922 target: target_a, ..
923 },
924 Rename {
925 target: target_b, ..
926 },
927 )
928 | (
929 ChangeVisibility {
930 target: target_a, ..
931 },
932 AddDerive {
933 target: target_b, ..
934 },
935 )
936 | (
937 ChangeVisibility {
938 target: target_a, ..
939 },
940 RemoveDerive {
941 target: target_b, ..
942 },
943 )
944 | (
945 ChangeVisibility {
946 target: target_a, ..
947 },
948 Rename {
949 target: target_b, ..
950 },
951 )
952 | (
953 Rename {
954 target: target_a, ..
955 },
956 AddField {
957 target: target_b, ..
958 },
959 )
960 | (
961 Rename {
962 target: target_a, ..
963 },
964 RemoveField {
965 target: target_b, ..
966 },
967 )
968 | (
969 Rename {
970 target: target_a, ..
971 },
972 AddVariant {
973 target: target_b, ..
974 },
975 )
976 | (
977 Rename {
978 target: target_a, ..
979 },
980 RemoveVariant {
981 target: target_b, ..
982 },
983 )
984 | (
985 Rename {
986 target: target_a, ..
987 },
988 AddMethod {
989 target: target_b, ..
990 },
991 )
992 | (
993 Rename {
994 target: target_a, ..
995 },
996 RemoveMethod {
997 target: target_b, ..
998 },
999 )
1000 | (
1001 Rename {
1002 target: target_a, ..
1003 },
1004 AddDerive {
1005 target: target_b, ..
1006 },
1007 )
1008 | (
1009 Rename {
1010 target: target_a, ..
1011 },
1012 RemoveDerive {
1013 target: target_b, ..
1014 },
1015 )
1016 | (
1017 Rename {
1018 target: target_a, ..
1019 },
1020 ChangeVisibility {
1021 target: target_b, ..
1022 },
1023 ) => target_conflicts(target_a, target_b),
1024
1025 _ => false,
1028 }
1029}
1030
1031pub fn find_conflicting_pairs(specs: &[MutationSpec]) -> Vec<(usize, usize)> {
1064 let mut conflicts = Vec::new();
1065 for i in 0..specs.len() {
1066 for j in (i + 1)..specs.len() {
1067 if specs_conflict(&specs[i], &specs[j]) {
1068 conflicts.push((i, j));
1069 }
1070 }
1071 }
1072 conflicts
1073}
1074
1075pub fn group_by_conflicts(specs: &[MutationSpec]) -> Vec<Vec<usize>> {
1120 if specs.is_empty() {
1121 return vec![];
1122 }
1123
1124 let mut parent: Vec<usize> = (0..specs.len()).collect();
1126 let mut rank: Vec<usize> = vec![0; specs.len()];
1127
1128 fn find(parent: &mut [usize], i: usize) -> usize {
1129 if parent[i] != i {
1130 parent[i] = find(parent, parent[i]); }
1132 parent[i]
1133 }
1134
1135 fn union(parent: &mut [usize], rank: &mut [usize], i: usize, j: usize) {
1136 let root_i = find(parent, i);
1137 let root_j = find(parent, j);
1138 if root_i != root_j {
1139 if rank[root_i] < rank[root_j] {
1141 parent[root_i] = root_j;
1142 } else if rank[root_i] > rank[root_j] {
1143 parent[root_j] = root_i;
1144 } else {
1145 parent[root_j] = root_i;
1146 rank[root_i] += 1;
1147 }
1148 }
1149 }
1150
1151 for i in 0..specs.len() {
1153 for j in (i + 1)..specs.len() {
1154 if specs_conflict(&specs[i], &specs[j]) {
1155 union(&mut parent, &mut rank, i, j);
1156 }
1157 }
1158 }
1159
1160 let mut groups: HashMap<usize, Vec<usize>> = HashMap::new();
1162 for i in 0..specs.len() {
1163 let root = find(&mut parent, i);
1164 groups.entry(root).or_default().push(i);
1165 }
1166
1167 groups.into_values().collect()
1168}
1169
1170#[cfg(test)]
1171mod tests {
1172 use super::*;
1173 use ryo_symbol::SymbolId;
1174
1175 fn dummy_id(index: u32) -> SymbolId {
1176 SymbolId::parse(&format!("{}v1", index)).expect("valid dummy id")
1177 }
1178
1179 #[test]
1180 fn test_target_conflicts_same_id() {
1181 let id1 = dummy_id(1);
1182 let target_a = MutationTargetSymbol::ById(id1);
1183 let target_b = MutationTargetSymbol::ById(id1);
1184
1185 assert!(target_conflicts(&target_a, &target_b));
1186 }
1187
1188 #[test]
1189 fn test_target_conflicts_different_ids() {
1190 let id1 = dummy_id(1);
1191 let id2 = dummy_id(2);
1192 let target_a = MutationTargetSymbol::ById(id1);
1193 let target_b = MutationTargetSymbol::ById(id2);
1194
1195 assert!(!target_conflicts(&target_a, &target_b));
1196 }
1197
1198 #[test]
1199 fn test_specs_conflict_same_target_same_field() {
1200 let id1 = dummy_id(1);
1201 let spec_a = MutationSpec::AddField {
1202 target: MutationTargetSymbol::ById(id1),
1203 field_name: "name".into(),
1204 field_type: "String".into(),
1205 visibility: Default::default(),
1206 };
1207 let spec_b = MutationSpec::AddField {
1208 target: MutationTargetSymbol::ById(id1),
1209 field_name: "name".into(), field_type: "String".into(),
1211 visibility: Default::default(),
1212 };
1213
1214 assert!(specs_conflict(&spec_a, &spec_b));
1215 }
1216
1217 #[test]
1218 fn test_specs_no_conflict_same_target_different_fields() {
1219 let id1 = dummy_id(1);
1220 let spec_a = MutationSpec::AddField {
1221 target: MutationTargetSymbol::ById(id1),
1222 field_name: "name".into(),
1223 field_type: "String".into(),
1224 visibility: Default::default(),
1225 };
1226 let spec_b = MutationSpec::AddField {
1227 target: MutationTargetSymbol::ById(id1),
1228 field_name: "email".into(), field_type: "String".into(),
1230 visibility: Default::default(),
1231 };
1232
1233 assert!(!specs_conflict(&spec_a, &spec_b));
1234 }
1235
1236 #[test]
1237 fn test_specs_conflict_different_targets() {
1238 let id1 = dummy_id(1);
1239 let id2 = dummy_id(2);
1240 let spec_a = MutationSpec::AddField {
1241 target: MutationTargetSymbol::ById(id1),
1242 field_name: "name".into(),
1243 field_type: "String".into(),
1244 visibility: Default::default(),
1245 };
1246 let spec_b = MutationSpec::AddField {
1247 target: MutationTargetSymbol::ById(id2),
1248 field_name: "email".into(),
1249 field_type: "String".into(),
1250 visibility: Default::default(),
1251 };
1252
1253 assert!(!specs_conflict(&spec_a, &spec_b));
1254 }
1255
1256 #[test]
1257 fn test_group_by_conflicts_independent() {
1258 let id1 = dummy_id(1);
1259 let id2 = dummy_id(2);
1260
1261 let specs = vec![
1262 MutationSpec::AddField {
1263 target: MutationTargetSymbol::ById(id1),
1264 field_name: "name".into(),
1265 field_type: "String".into(),
1266 visibility: Default::default(),
1267 },
1268 MutationSpec::AddField {
1269 target: MutationTargetSymbol::ById(id2),
1270 field_name: "email".into(),
1271 field_type: "String".into(),
1272 visibility: Default::default(),
1273 },
1274 ];
1275
1276 let groups = group_by_conflicts(&specs);
1277 assert_eq!(groups.len(), 2); }
1279
1280 #[test]
1281 fn test_group_by_conflicts_conflicting() {
1282 let id1 = dummy_id(1);
1283
1284 let specs = vec![
1285 MutationSpec::AddField {
1286 target: MutationTargetSymbol::ById(id1),
1287 field_name: "name".into(),
1288 field_type: "String".into(),
1289 visibility: Default::default(),
1290 },
1291 MutationSpec::AddField {
1292 target: MutationTargetSymbol::ById(id1),
1293 field_name: "name".into(), field_type: "i32".into(),
1295 visibility: Default::default(),
1296 },
1297 ];
1298
1299 let groups = group_by_conflicts(&specs);
1300 assert_eq!(groups.len(), 1); assert_eq!(groups[0].len(), 2);
1302 }
1303
1304 #[test]
1305 fn test_cross_variant_conflict() {
1306 let id1 = dummy_id(1);
1307
1308 let spec_add = MutationSpec::AddField {
1309 target: MutationTargetSymbol::ById(id1),
1310 field_name: "name".into(),
1311 field_type: "String".into(),
1312 visibility: Default::default(),
1313 };
1314
1315 let spec_remove = MutationSpec::RemoveItem {
1316 target: MutationTargetSymbol::ById(id1),
1317 item_kind: ryo_source::ItemKind::Struct,
1318 };
1319
1320 assert!(specs_conflict(&spec_add, &spec_remove));
1322 }
1323}