1use 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
155 iter::once(pos)
156 .chain(interleave(backward, forward))
157 .find(|&pos| match_fragment(image, hunk.lines(), pos))
158}
159
160fn pre_image_line_count<T: ?Sized>(lines: &[Line<'_, T>]) -> usize {
161 pre_image(lines).count()
162}
163
164fn post_image<'a, 'b, T: ?Sized>(lines: &'b [Line<'a, T>]) -> impl Iterator<Item = &'a T> + 'b {
165 lines.iter().filter_map(|line| match line {
166 Line::Context(l) | Line::Insert(l) => Some(*l),
167 Line::Delete(_) => None,
168 })
169}
170
171fn pre_image<'a, 'b, T: ?Sized>(lines: &'b [Line<'a, T>]) -> impl Iterator<Item = &'a T> + 'b {
172 lines.iter().filter_map(|line| match line {
173 Line::Context(l) | Line::Delete(l) => Some(*l),
174 Line::Insert(_) => None,
175 })
176}
177
178fn match_fragment<T: PartialEq + ?Sized>(
179 image: &[ImageLine<T>],
180 lines: &[Line<'_, T>],
181 pos: usize,
182) -> bool {
183 let len = pre_image_line_count(lines);
184
185 let image = if let Some(image) = image.get(pos..pos + len) {
186 image
187 } else {
188 return false;
189 };
190
191 if image.iter().any(ImageLine::is_patched) {
193 return false;
194 }
195
196 pre_image(lines).eq(image.iter().map(ImageLine::inner))
197}
198
199#[derive(Debug)]
200struct Interleave<I, J> {
201 a: iter::Fuse<I>,
202 b: iter::Fuse<J>,
203 flag: bool,
204}
205
206fn interleave<I, J>(
207 i: I,
208 j: J,
209) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
210where
211 I: IntoIterator,
212 J: IntoIterator<Item = I::Item>,
213{
214 Interleave {
215 a: i.into_iter().fuse(),
216 b: j.into_iter().fuse(),
217 flag: false,
218 }
219}
220
221impl<I, J> Iterator for Interleave<I, J>
222where
223 I: Iterator,
224 J: Iterator<Item = I::Item>,
225{
226 type Item = I::Item;
227
228 fn next(&mut self) -> Option<I::Item> {
229 self.flag = !self.flag;
230 if self.flag {
231 match self.a.next() {
232 None => self.b.next(),
233 item => item,
234 }
235 } else {
236 match self.b.next() {
237 None => self.a.next(),
238 item => item,
239 }
240 }
241 }
242}