@@ -4,6 +4,7 @@ use crate::{
44 utils:: Classifier ,
55} ;
66use std:: { cmp, fmt} ;
7+ use std:: hash:: Hash ;
78
89#[ cfg( test) ]
910mod tests;
@@ -205,6 +206,34 @@ impl MergeOptions {
205206 self . style ,
206207 )
207208 }
209+
210+ pub fn merge_custom < ' a , T : Eq + Hash > (
211+ & self ,
212+ ancestor : & ' a [ T ] ,
213+ ours : & ' a [ T ] ,
214+ theirs : & ' a [ T ] ,
215+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
216+ let mut classifier = Classifier :: default ( ) ;
217+ let ( ancestor_lines, ancestor_ids) = classifier. classify ( ancestor) ;
218+ let ( our_lines, our_ids) = classifier. classify ( ours) ;
219+ let ( their_lines, their_ids) = classifier. classify ( theirs) ;
220+
221+ let opts = DiffOptions :: default ( ) ;
222+ let our_solution = opts. diff_slice ( & ancestor_ids, & our_ids) ;
223+ let their_solution = opts. diff_slice ( & ancestor_ids, & their_ids) ;
224+
225+ let merged = merge_solutions ( & our_solution, & their_solution) ;
226+ let mut merge = diff3_range_to_merge_range ( & merged) ;
227+
228+ cleanup_conflicts ( & mut merge) ;
229+
230+ output_result_custom (
231+ & ancestor_lines,
232+ & our_lines,
233+ & their_lines,
234+ & merge,
235+ )
236+ }
208237}
209238
210239impl Default for MergeOptions {
@@ -277,6 +306,30 @@ pub fn merge_bytes<'a>(
277306 MergeOptions :: default ( ) . merge_bytes ( ancestor, ours, theirs)
278307}
279308
309+ /// Infos about a merge that went wrong
310+ #[ derive( Debug , Ord , PartialOrd , Eq , PartialEq ) ]
311+ pub struct MergeConflicts {
312+ /// How many conflicts have occurred
313+ pub count : usize
314+ }
315+
316+ impl std:: fmt:: Display for MergeConflicts {
317+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
318+ write ! ( f, "{} merge conflicts" , self . count)
319+ }
320+ }
321+
322+ impl std:: error:: Error for MergeConflicts { }
323+
324+ /// Perform a 3-way merge between any list of values that support it
325+ pub fn merge_custom < ' a , T : Eq + Hash > (
326+ ancestor : & ' a [ T ] ,
327+ ours : & ' a [ T ] ,
328+ theirs : & ' a [ T ] ,
329+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
330+ MergeOptions :: default ( ) . merge_custom ( ancestor, ours, theirs)
331+ }
332+
280333fn merge_solutions < ' ancestor , ' ours , ' theirs , T : ?Sized + SliceLike > (
281334 our_solution : & [ DiffRange < ' ancestor , ' ours , T > ] ,
282335 their_solution : & [ DiffRange < ' ancestor , ' theirs , T > ] ,
@@ -631,3 +684,39 @@ fn add_conflict_marker_bytes(
631684 }
632685 output. push ( b'\n' ) ;
633686}
687+
688+ fn output_result_custom < ' a , T : Eq + Hash > (
689+ ancestor : & [ & ' a T ] ,
690+ ours : & [ & ' a T ] ,
691+ theirs : & [ & ' a T ] ,
692+ merge : & [ MergeRange < [ u64 ] > ] ,
693+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
694+ let mut conflicts = 0 ;
695+ let mut output = Vec :: new ( ) ;
696+
697+ for merge_range in merge {
698+ match merge_range {
699+ MergeRange :: Equal ( range, ..) => {
700+ output. extend ( ancestor[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
701+ }
702+ MergeRange :: Conflict ( _ancestor_range, _ours_range, _theirs_range) => {
703+ conflicts += 1 ;
704+ }
705+ MergeRange :: Ours ( range) => {
706+ output. extend ( ours[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
707+ }
708+ MergeRange :: Theirs ( range) => {
709+ output. extend ( theirs[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
710+ }
711+ MergeRange :: Both ( range, _) => {
712+ output. extend ( ours[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
713+ }
714+ }
715+ }
716+
717+ if conflicts != 0 {
718+ Err ( MergeConflicts { count : conflicts } )
719+ } else {
720+ Ok ( output)
721+ }
722+ }
0 commit comments