gix_diff/tree_with_rewrites/
function.rs1use bstr::BStr;
2use gix_object::TreeRefIter;
3
4use super::{Action, ChangeRef, Error, Options};
5use crate::{rewrites, rewrites::tracker};
6
7pub fn diff<E>(
25 lhs: TreeRefIter<'_>,
26 rhs: TreeRefIter<'_>,
27 resource_cache: &mut crate::blob::Platform,
28 tree_diff_state: &mut crate::tree::State,
29 objects: &impl gix_object::FindObjectOrHeader,
30 for_each: impl FnMut(ChangeRef<'_>) -> Result<Action, E>,
31 options: Options,
32) -> Result<Option<rewrites::Outcome>, Error>
33where
34 E: Into<Box<dyn std::error::Error + Sync + Send + 'static>>,
35{
36 let mut delegate = Delegate {
37 src_tree: lhs,
38 recorder: crate::tree::Recorder::default().track_location(options.location),
39 visit: for_each,
40 location: options.location,
41 objects,
42 tracked: options.rewrites.map(rewrites::Tracker::new),
43 err: None,
44 };
45 match crate::tree(lhs, rhs, tree_diff_state, objects, &mut delegate) {
46 Ok(()) => {
47 let outcome = delegate.process_tracked_changes(resource_cache)?;
48 match delegate.err {
49 Some(err) => Err(Error::ForEach(err.into())),
50 None => Ok(outcome),
51 }
52 }
53 Err(crate::tree::Error::Cancelled) => delegate
54 .err
55 .map_or(Err(Error::Diff(crate::tree::Error::Cancelled)), |err| {
56 Err(Error::ForEach(err.into()))
57 }),
58 Err(err) => Err(err.into()),
59 }
60}
61
62struct Delegate<'a, 'old, VisitFn, E, Objects> {
63 src_tree: TreeRefIter<'old>,
64 recorder: crate::tree::Recorder,
65 objects: &'a Objects,
66 visit: VisitFn,
67 tracked: Option<rewrites::Tracker<crate::tree::visit::Change>>,
68 location: Option<crate::tree::recorder::Location>,
69 err: Option<E>,
70}
71
72impl<VisitFn, E, Objects> Delegate<'_, '_, VisitFn, E, Objects>
73where
74 Objects: gix_object::FindObjectOrHeader,
75 VisitFn: for<'delegate> FnMut(ChangeRef<'_>) -> Result<Action, E>,
76 E: Into<Box<dyn std::error::Error + Sync + Send + 'static>>,
77{
78 fn emit_change(
80 change: crate::tree::visit::Change,
81 location: &BStr,
82 visit: &mut VisitFn,
83 stored_err: &mut Option<E>,
84 ) -> crate::tree::visit::Action {
85 use crate::tree::visit::Change::*;
86 let change = match change {
87 Addition {
88 entry_mode,
89 oid,
90 relation,
91 } => ChangeRef::Addition {
92 location,
93 relation,
94 entry_mode,
95 id: oid,
96 },
97 Deletion {
98 entry_mode,
99 oid,
100 relation,
101 } => ChangeRef::Deletion {
102 entry_mode,
103 location,
104 relation,
105 id: oid,
106 },
107 Modification {
108 previous_entry_mode,
109 previous_oid,
110 entry_mode,
111 oid,
112 } => ChangeRef::Modification {
113 location,
114 previous_entry_mode,
115 entry_mode,
116 previous_id: previous_oid,
117 id: oid,
118 },
119 };
120 match visit(change) {
121 Ok(Action::Cancel) => crate::tree::visit::Action::Cancel,
122 Ok(Action::Continue) => crate::tree::visit::Action::Continue,
123 Err(err) => {
124 *stored_err = Some(err);
125 crate::tree::visit::Action::Cancel
126 }
127 }
128 }
129
130 fn process_tracked_changes(
131 &mut self,
132 diff_cache: &mut crate::blob::Platform,
133 ) -> Result<Option<rewrites::Outcome>, Error> {
134 use crate::rewrites::tracker::Change as _;
135 let tracked = match self.tracked.as_mut() {
136 Some(t) => t,
137 None => return Ok(None),
138 };
139
140 let outcome = tracked.emit(
141 |dest, source| match source {
142 Some(source) => {
143 let (oid, mode) = dest.change.oid_and_entry_mode();
144 let change = ChangeRef::Rewrite {
145 source_location: source.location,
146 source_entry_mode: source.entry_mode,
147 source_id: source.id,
148 source_relation: source.change.relation(),
149 entry_mode: mode,
150 id: oid.to_owned(),
151 relation: dest.change.relation(),
152 diff: source.diff,
153 location: dest.location,
154 copy: match source.kind {
155 tracker::visit::SourceKind::Rename => false,
156 tracker::visit::SourceKind::Copy => true,
157 },
158 };
159 match (self.visit)(change) {
160 Ok(Action::Cancel) => crate::tree::visit::Action::Cancel,
161 Ok(Action::Continue) => crate::tree::visit::Action::Continue,
162 Err(err) => {
163 self.err = Some(err);
164 crate::tree::visit::Action::Cancel
165 }
166 }
167 }
168 None => Self::emit_change(dest.change, dest.location, &mut self.visit, &mut self.err),
169 },
170 diff_cache,
171 self.objects,
172 |push| {
173 let mut delegate = tree_to_changes::Delegate::new(push, self.location);
174 let state = gix_traverse::tree::breadthfirst::State::default();
175 gix_traverse::tree::breadthfirst(self.src_tree, state, self.objects, &mut delegate)
176 },
177 )?;
178 Ok(Some(outcome))
179 }
180}
181
182impl<VisitFn, E, Objects> crate::tree::Visit for Delegate<'_, '_, VisitFn, E, Objects>
183where
184 Objects: gix_object::FindObjectOrHeader,
185 VisitFn: for<'delegate> FnMut(ChangeRef<'_>) -> Result<Action, E>,
186 E: Into<Box<dyn std::error::Error + Sync + Send + 'static>>,
187{
188 fn pop_front_tracked_path_and_set_current(&mut self) {
189 self.recorder.pop_front_tracked_path_and_set_current();
190 }
191
192 fn push_back_tracked_path_component(&mut self, component: &BStr) {
193 self.recorder.push_back_tracked_path_component(component);
194 }
195
196 fn push_path_component(&mut self, component: &BStr) {
197 self.recorder.push_path_component(component);
198 }
199
200 fn pop_path_component(&mut self) {
201 self.recorder.pop_path_component();
202 }
203
204 fn visit(&mut self, change: crate::tree::visit::Change) -> crate::tree::visit::Action {
205 match self.tracked.as_mut() {
206 Some(tracked) => tracked
207 .try_push_change(change, self.recorder.path())
208 .map_or(crate::tree::visit::Action::Continue, |change| {
209 Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err)
210 }),
211 None => Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err),
212 }
213 }
214}
215
216mod tree_to_changes {
217 use bstr::BStr;
218 use gix_object::tree::EntryRef;
219
220 use crate::tree::visit::Change;
221
222 pub struct Delegate<'a> {
223 push: &'a mut dyn FnMut(Change, &BStr),
224 recorder: gix_traverse::tree::Recorder,
225 }
226
227 impl<'a> Delegate<'a> {
228 pub fn new(push: &'a mut dyn FnMut(Change, &BStr), location: Option<crate::tree::recorder::Location>) -> Self {
229 let location = location.map(|t| match t {
230 crate::tree::recorder::Location::FileName => gix_traverse::tree::recorder::Location::FileName,
231 crate::tree::recorder::Location::Path => gix_traverse::tree::recorder::Location::Path,
232 });
233 Self {
234 push,
235 recorder: gix_traverse::tree::Recorder::default().track_location(location),
236 }
237 }
238 }
239
240 impl gix_traverse::tree::Visit for Delegate<'_> {
241 fn pop_back_tracked_path_and_set_current(&mut self) {
242 self.recorder.pop_back_tracked_path_and_set_current();
243 }
244
245 fn pop_front_tracked_path_and_set_current(&mut self) {
246 self.recorder.pop_front_tracked_path_and_set_current();
247 }
248
249 fn push_back_tracked_path_component(&mut self, component: &BStr) {
250 self.recorder.push_back_tracked_path_component(component);
251 }
252
253 fn push_path_component(&mut self, component: &BStr) {
254 self.recorder.push_path_component(component);
255 }
256
257 fn pop_path_component(&mut self) {
258 self.recorder.pop_path_component();
259 }
260
261 fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
262 gix_traverse::tree::visit::Action::Continue
263 }
264
265 fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
266 if entry.mode.is_blob() {
267 (self.push)(
268 Change::Modification {
269 previous_entry_mode: entry.mode,
270 previous_oid: gix_hash::ObjectId::null(entry.oid.kind()),
271 entry_mode: entry.mode,
272 oid: entry.oid.to_owned(),
273 },
274 self.recorder.path(),
275 );
276 }
277 gix_traverse::tree::visit::Action::Continue
278 }
279 }
280}