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