java_diff_utils_rs/patch/
delta.rs1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use super::chunk::Chunk;
7use super::delta_type::DeltaType;
8use super::error::PatchError;
9use super::verify_chunk::VerifyChunk;
10
11#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
13#[serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
14pub struct Delta<T> {
15 delta_type: DeltaType,
16 source: Chunk<T>,
17 target: Chunk<T>,
18}
19
20impl<T> Delta<T> {
21 pub fn new(delta_type: DeltaType, source: Chunk<T>, target: Chunk<T>) -> Self {
23 Self {
24 delta_type,
25 source,
26 target,
27 }
28 }
29
30 #[inline]
32 pub fn source(&self) -> &Chunk<T> {
33 &self.source
34 }
35
36 #[inline]
38 pub fn source_mut(&mut self) -> &mut Chunk<T> {
39 &mut self.source
40 }
41
42 #[inline]
44 pub fn target(&self) -> &Chunk<T> {
45 &self.target
46 }
47
48 #[inline]
50 pub fn target_mut(&mut self) -> &mut Chunk<T> {
51 &mut self.target
52 }
53
54 #[inline]
56 pub fn delta_type(&self) -> DeltaType {
57 self.delta_type
58 }
59
60 pub fn verify_chunk_to_fit_target(&self, target: &[T]) -> Result<VerifyChunk, PatchError>
62 where
63 T: PartialEq,
64 {
65 self.source.verify_chunk(target)
66 }
67
68 pub fn verify_and_apply_to(&self, target: &mut Vec<T>) -> Result<VerifyChunk, PatchError>
70 where
71 T: Clone + PartialEq,
72 {
73 let verify = self.verify_chunk_to_fit_target(target)?;
74 if verify == VerifyChunk::Ok {
75 self.apply_to(target)?;
76 }
77 Ok(verify)
78 }
79
80 pub fn apply_to(&self, target: &mut Vec<T>) -> Result<(), PatchError>
82 where
83 T: Clone + PartialEq,
84 {
85 self.apply_at(target, self.source.position())
86 }
87
88 pub fn apply_at(&self, target: &mut Vec<T>, pos: usize) -> Result<(), PatchError>
90 where
91 T: Clone + PartialEq,
92 {
93 if pos > target.len() {
94 return Err(PatchError::PatchFailed(format!(
95 "Patch position {} out of bounds for target length {}",
96 pos,
97 target.len()
98 )));
99 }
100
101 match self.delta_type {
102 DeltaType::Delete => {
103 let len = self.source.len();
104 if pos + len > target.len() {
105 return Err(PatchError::PatchFailed(format!(
106 "Delete delta range [{}..{}] exceeds target length {}",
107 pos,
108 pos + len,
109 target.len()
110 )));
111 }
112 target.drain(pos..pos + len);
113 }
114 DeltaType::Insert => {
115 let lines = self.target.lines();
116 target.splice(pos..pos, lines.iter().cloned());
117 }
118 DeltaType::Change => {
119 let len = self.source.len();
120 if pos + len > target.len() {
121 return Err(PatchError::PatchFailed(format!(
122 "Change delta source range [{}..{}] exceeds target length {}",
123 pos,
124 pos + len,
125 target.len()
126 )));
127 }
128 target.splice(pos..pos + len, self.target.lines().iter().cloned());
129 }
130 DeltaType::Equal => {}
131 }
132
133 Ok(())
134 }
135
136 pub fn restore(&self, target: &mut Vec<T>) -> Result<(), PatchError>
138 where
139 T: Clone + PartialEq,
140 {
141 let pos = self.target.position();
142
143 match self.delta_type {
144 DeltaType::Delete => {
145 let lines = self.source.lines();
146 target.splice(pos..pos, lines.iter().cloned());
147 }
148 DeltaType::Insert => {
149 let len = self.target.len();
150 if pos + len > target.len() {
151 return Err(PatchError::PatchFailed(format!(
152 "Restore insert delta range [{}..{}] exceeds target length {}",
153 pos,
154 pos + len,
155 target.len()
156 )));
157 }
158 target.drain(pos..pos + len);
159 }
160 DeltaType::Change => {
161 let len = self.target.len();
162 if pos + len > target.len() {
163 return Err(PatchError::PatchFailed(format!(
164 "Restore change delta target range [{}..{}] exceeds target length {}",
165 pos,
166 pos + len,
167 target.len()
168 )));
169 }
170 target.splice(pos..pos + len, self.source.lines().iter().cloned());
171 }
172 DeltaType::Equal => {}
173 }
174
175 Ok(())
176 }
177
178 pub fn apply_fuzzy_to_at(
181 &self,
182 target: &mut Vec<T>,
183 _fuzz: usize,
184 position: usize,
185 ) -> Result<(), PatchError>
186 where
187 T: Clone + PartialEq,
188 {
189 if position > target.len() {
190 return Err(PatchError::PatchFailed(format!(
191 "Fuzzy patch position {} out of bounds for target length {}",
192 position,
193 target.len()
194 )));
195 }
196
197 match self.delta_type {
198 DeltaType::Delete => {
199 let src_len = self.source.len();
200 let end = (position + src_len).min(target.len());
201 if position < end {
202 target.drain(position..end);
203 }
204 Ok(())
205 }
206 DeltaType::Insert => {
207 if _fuzz > 0 {
208 return Err(PatchError::UnsupportedOperation(
209 "Fuzzy patching is not supported for InsertDelta".to_string(),
210 ));
211 }
212 let insert_pos = position.min(target.len());
213 let lines = self.target.lines();
214 target.splice(insert_pos..insert_pos, lines.iter().cloned());
215 Ok(())
216 }
217 DeltaType::Change => {
218 let src_len = self.source.len();
219 let target_lines = self.target.lines();
220 let end = (position + src_len).min(target.len());
221
222 target.splice(position..end, target_lines.iter().cloned());
223 Ok(())
224 }
225 DeltaType::Equal => Ok(()),
226 }
227 }
228
229 #[must_use]
231 pub fn with_chunks(&self, source: Chunk<T>, target: Chunk<T>) -> Self {
232 Self {
233 delta_type: self.delta_type,
234 source,
235 target,
236 }
237 }
238}
239
240impl<T> AsRef<Delta<T>> for Delta<T> {
241 fn as_ref(&self) -> &Delta<T> {
242 self
243 }
244}
245
246impl<T> From<Box<Delta<T>>> for Delta<T> {
247 fn from(boxed: Box<Delta<T>>) -> Self {
248 *boxed
249 }
250}
251
252fn format_lines<T: fmt::Display>(lines: &[T]) -> String {
253 let formatted_items = lines
254 .iter()
255 .map(|item| item.to_string())
256 .collect::<Vec<_>>()
257 .join(", ");
258 format!("[{}]", formatted_items)
259}
260
261impl<T: fmt::Display> fmt::Display for Delta<T> {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 match self.delta_type {
264 DeltaType::Insert => write!(
265 f,
266 "[InsertDelta, position: {}, lines: {}]",
267 self.source().position(),
268 format_lines(self.target().lines())
269 ),
270 DeltaType::Delete => write!(
271 f,
272 "[DeleteDelta, position: {}, lines: {}]",
273 self.source().position(),
274 format_lines(self.source().lines())
275 ),
276 DeltaType::Change => write!(
277 f,
278 "[ChangeDelta, position: {}, lines: {} to {}]",
279 self.source().position(),
280 format_lines(self.source().lines()),
281 format_lines(self.target().lines())
282 ),
283 DeltaType::Equal => write!(
284 f,
285 "[EqualDelta, position: {}, lines: {}]",
286 self.source().position(),
287 format_lines(self.source().lines())
288 ),
289 }
290 }
291}