diffy_fork_filenames/
apply.rs1use crate::{
2 patch::{Hunk, Line, Patch},
3 utils::LineIter,
4};
5use std::{fmt, iter};
6
7#[derive(Debug)]
11pub struct ApplyError(usize);
12
13impl fmt::Display for ApplyError {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 write!(f, "error applying hunk #{}", self.0)
16 }
17}
18
19impl std::error::Error for ApplyError {}
20
21#[derive(Debug)]
22enum ImageLine<'a, T: ?Sized> {
23 Unpatched(&'a T),
24 Patched(&'a T),
25}
26
27impl<'a, T: ?Sized> ImageLine<'a, T> {
28 fn inner(&self) -> &'a T {
29 match self {
30 ImageLine::Unpatched(inner) | ImageLine::Patched(inner) => inner,
31 }
32 }
33
34 fn into_inner(self) -> &'a T {
35 self.inner()
36 }
37
38 fn is_patched(&self) -> bool {
39 match self {
40 ImageLine::Unpatched(_) => false,
41 ImageLine::Patched(_) => true,
42 }
43 }
44}
45
46impl<T: ?Sized> Copy for ImageLine<'_, T> {}
47
48impl<T: ?Sized> Clone for ImageLine<'_, T> {
49 fn clone(&self) -> Self {
50 *self
51 }
52}
53
54pub fn apply(base_image: &str, patch: &Patch<'_, str>) -> Result<String, ApplyError> {
92 let mut image: Vec<_> = LineIter::new(base_image)
93 .map(ImageLine::Unpatched)
94 .collect();
95
96 for (i, hunk) in patch.hunks().iter().enumerate() {
97 apply_hunk(&mut image, hunk).map_err(|_| ApplyError(i + 1))?;
98 }
99
100 Ok(image.into_iter().map(ImageLine::into_inner).collect())
101}
102
103pub fn apply_bytes(base_image: &[u8], patch: &Patch<'_, [u8]>) -> Result<Vec<u8>, ApplyError> {
105 let mut image: Vec<_> = LineIter::new(base_image)
106 .map(ImageLine::Unpatched)
107 .collect();
108
109 for (i, hunk) in patch.hunks().iter().enumerate() {
110 apply_hunk(&mut image, hunk).map_err(|_| ApplyError(i + 1))?;
111 }
112
113 Ok(image
114 .into_iter()
115 .flat_map(ImageLine::into_inner)
116 .copied()
117 .collect())
118}
119
120fn apply_hunk<'a, T: PartialEq + ?Sized>(
121 image: &mut Vec<ImageLine<'a, T>>,
122 hunk: &Hunk<'a, T>,
123) -> Result<(), ()> {
124 let pos = find_position(image, hunk).ok_or(())?;
126
127 image.splice(
129 pos..pos + pre_image_line_count(hunk.lines()),
130 post_image(hunk.lines()).map(ImageLine::Patched),
131 );
132
133 Ok(())
134}
135
136fn find_position<T: PartialEq + ?Sized>(
143 image: &[ImageLine<T>],
144 hunk: &Hunk<'_, T>,
145) -> Option<usize> {
146 let pos = std::cmp::min(hunk.new_range().start().saturating_sub(1), image.len());
149
150 let backward = (0..pos).rev();
153 let forward = pos + 1..image.len();
154 for pos in iter::once(pos).chain(interleave(backward, forward)) {
155 if match_fragment(image, hunk.lines(), pos) {
156 return Some(pos);
157 }
158 }
159
160 None
161}
162
163fn pre_image_line_count<T: ?Sized>(lines: &[Line<'_, T>]) -> usize {
164 pre_image(lines).count()
165}
166
167fn post_image<'a, 'b, T: ?Sized>(lines: &'b [Line<'a, T>]) -> impl Iterator<Item = &'a T> + 'b {
168 lines.iter().filter_map(|line| match line {
169 Line::Context(l) | Line::Insert(l) => Some(*l),
170 Line::Delete(_) => None,
171 })
172}
173
174fn pre_image<'a, 'b, T: ?Sized>(lines: &'b [Line<'a, T>]) -> impl Iterator<Item = &'a T> + 'b {
175 lines.iter().filter_map(|line| match line {
176 Line::Context(l) | Line::Delete(l) => Some(*l),
177 Line::Insert(_) => None,
178 })
179}
180
181fn match_fragment<T: PartialEq + ?Sized>(
182 image: &[ImageLine<T>],
183 lines: &[Line<'_, T>],
184 pos: usize,
185) -> bool {
186 let len = pre_image_line_count(lines);
187
188 let image = if let Some(image) = image.get(pos..pos + len) {
189 image
190 } else {
191 return false;
192 };
193
194 if image.iter().any(ImageLine::is_patched) {
196 return false;
197 }
198
199 pre_image(lines).eq(image.iter().map(ImageLine::inner))
200}
201
202#[derive(Debug)]
203struct Interleave<I, J> {
204 a: iter::Fuse<I>,
205 b: iter::Fuse<J>,
206 flag: bool,
207}
208
209fn interleave<I, J>(
210 i: I,
211 j: J,
212) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
213where
214 I: IntoIterator,
215 J: IntoIterator<Item = I::Item>,
216{
217 Interleave {
218 a: i.into_iter().fuse(),
219 b: j.into_iter().fuse(),
220 flag: false,
221 }
222}
223
224impl<I, J> Iterator for Interleave<I, J>
225where
226 I: Iterator,
227 J: Iterator<Item = I::Item>,
228{
229 type Item = I::Item;
230
231 fn next(&mut self) -> Option<I::Item> {
232 self.flag = !self.flag;
233 if self.flag {
234 match self.a.next() {
235 None => self.b.next(),
236 item => item,
237 }
238 } else {
239 match self.b.next() {
240 None => self.a.next(),
241 item => item,
242 }
243 }
244 }
245}