Skip to main content

java_diff_utils_rs/patch/
delta.rs

1//! Delta representation of sequence modifications between target lists.
2
3use 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/// Represents a single modification delta between a source chunk and a target chunk.
12#[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    /// Creates a new `Delta` with the specified type, source, and target chunks.
22    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    /// Returns a reference to the source chunk.
31    #[inline]
32    pub fn source(&self) -> &Chunk<T> {
33        &self.source
34    }
35
36    /// Returns a mutable reference to the source chunk.
37    #[inline]
38    pub fn source_mut(&mut self) -> &mut Chunk<T> {
39        &mut self.source
40    }
41
42    /// Returns a reference to the target chunk.
43    #[inline]
44    pub fn target(&self) -> &Chunk<T> {
45        &self.target
46    }
47
48    /// Returns a mutable reference to the target chunk.
49    #[inline]
50    pub fn target_mut(&mut self) -> &mut Chunk<T> {
51        &mut self.target
52    }
53
54    /// Returns the type of this delta.
55    #[inline]
56    pub fn delta_type(&self) -> DeltaType {
57        self.delta_type
58    }
59
60    /// Verifies whether the source chunk of this delta fits the provided target sequence.
61    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    /// Verifies that the source chunk matches the target sequence and applies the delta in-place if valid.
69    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    /// Applies this delta to the target vector using its source chunk position.
81    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    /// Applies this delta to the target vector starting at an explicit position.
89    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    /// Restores (un-applies) this delta, reverting the target sequence back to its original state.
137    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    /// Applies fuzzy patch matching at a given position with context tolerances.
179    /// Applies fuzzy patch matching at a given position with context tolerances.
180    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    /// Returns a new instance of `Delta` with customized source and target chunk values.
230    #[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}