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