java_diff_utils_rs/
diff_utils.rs1use std::sync::RwLock;
4
5use crate::algorithm::myers::myers::MyersDiff;
6use crate::algorithm::{DiffAlgorithm, DiffAlgorithmFactory, DiffAlgorithmListener};
7use crate::patch::patch_failed_exception::PatchFailedException;
8use crate::patch::Patch;
9
10static DEFAULT_DIFF_FACTORY: RwLock<Option<Box<dyn DiffAlgorithmFactory<String> + Send + Sync>>> =
11 RwLock::new(None);
12
13pub struct DiffUtils;
14
15impl DiffUtils {
16 pub fn with_default_diff_algorithm_factory(
17 factory: Box<dyn DiffAlgorithmFactory<String> + Send + Sync>,
18 ) {
19 if let Ok(mut guard) = DEFAULT_DIFF_FACTORY.write() {
20 *guard = Some(factory);
21 }
22 }
23
24 pub fn diff<T>(
40 original: &[T],
41 revised: &[T],
42 progress: Option<&dyn DiffAlgorithmListener>,
43 ) -> Patch<T>
44 where
45 T: PartialEq + Clone + 'static,
46 {
47 let algo = Self::get_default_algorithm::<T>();
48 Self::diff_with_algorithm(original, revised, algo.as_ref(), progress, false)
49 }
50
51 pub fn diff_with_options<T>(
52 original: &[T],
53 revised: &[T],
54 include_equal_parts: bool,
55 ) -> Patch<T>
56 where
57 T: PartialEq + Clone + 'static,
58 {
59 let algo = Self::get_default_algorithm::<T>();
60 Self::diff_with_algorithm(original, revised, algo.as_ref(), None, include_equal_parts)
61 }
62
63 pub fn diff_text(
64 source_text: &str,
65 target_text: &str,
66 progress: Option<&dyn DiffAlgorithmListener>,
67 ) -> Patch<String> {
68 let original: Vec<String> = source_text.lines().map(|s| s.to_string()).collect();
69 let revised: Vec<String> = target_text.lines().map(|s| s.to_string()).collect();
70 Self::diff(&original, &revised, progress)
71 }
72
73 pub fn diff_with_equalizer<T, F>(source: &[T], target: &[T], equalizer: Option<F>) -> Patch<T>
87 where
88 T: PartialEq + Clone + 'static,
89 F: Fn(&T, &T) -> bool + Send + Sync + 'static,
90 {
91 if let Some(eq) = equalizer {
92 let algo = MyersDiff::with_equalizer(eq);
93 Self::diff_with_algorithm(source, target, &algo, None, false)
94 } else {
95 let algo = MyersDiff::default();
96 Self::diff_with_algorithm(source, target, &algo, None, false)
97 }
98 }
99
100 pub fn diff_with_algorithm<T>(
101 original: &[T],
102 revised: &[T],
103 algorithm: &dyn DiffAlgorithm<T>,
104 _progress: Option<&dyn DiffAlgorithmListener>,
105 include_equal_parts: bool,
106 ) -> Patch<T>
107 where
108 T: Clone + 'static,
109 {
110 let deltas = algorithm.diff(original, revised);
111 Patch::generate(original, revised, &deltas, include_equal_parts)
112 }
113
114 pub fn diff_inline(original: &str, revised: &str) -> Patch<String> {
115 let orig_list: Vec<String> = original.chars().map(|c| c.to_string()).collect();
116 let rev_list: Vec<String> = revised.chars().map(|c| c.to_string()).collect();
117
118 let mut patch = Self::diff(&orig_list, &rev_list, None);
119
120 for delta in patch.deltas_mut() {
121 let source_lines = Self::compress_lines(delta.source_mut().lines(), "");
122 delta.source_mut().set_lines(source_lines);
123
124 let target_lines = Self::compress_lines(delta.target_mut().lines(), "");
125 delta.target_mut().set_lines(target_lines);
126 }
127
128 patch
129 }
130
131 pub fn patch<T>(original: &[T], patch: &Patch<T>) -> Result<Vec<T>, PatchFailedException>
132 where
133 T: PartialEq + Clone,
134 {
135 patch.apply_to(original).map_err(PatchFailedException::from)
136 }
137
138 pub fn unpatch<T>(revised: &[T], patch: &Patch<T>) -> Result<Vec<T>, PatchFailedException>
139 where
140 T: PartialEq + Clone,
141 {
142 patch.restore(revised).map_err(PatchFailedException::from)
143 }
144
145 fn compress_lines(lines: &[String], delimiter: &str) -> Vec<String> {
146 if lines.is_empty() {
147 Vec::new()
148 } else {
149 vec![lines.join(delimiter)]
150 }
151 }
152
153 fn get_default_algorithm<T: PartialEq + Clone + 'static>() -> Box<dyn DiffAlgorithm<T>> {
154 Box::new(crate::algorithm::HistogramDiff::default())
155 }
156}