gix_diff/tree_with_rewrites/
function.rs1use bstr::BStr;
2use gix_error::ErrorExt;
3use gix_error::Result;
4use gix_object::TreeRefIter;
5
6use super::{Action, ChangeRef, Options};
7use crate::{rewrites, rewrites::tracker, tree::Error};
8
9pub fn diff(
27 lhs: TreeRefIter<'_>,
28 rhs: TreeRefIter<'_>,
29 resource_cache: &mut crate::blob::Platform,
30 tree_diff_state: &mut crate::tree::State,
31 objects: &impl gix_object::FindObjectOrHeader,
32 for_each: impl FnMut(ChangeRef<'_>) -> Result<Action>,
33 options: Options,
34) -> std::result::Result<Option<rewrites::Outcome>, Error> {
35 fn callback_error(err: gix_error::Error) -> Error {
36 Error::Failure(err.and_raise(gix_error::message("The user-provided callback failed")))
37 }
38
39 let mut delegate = Delegate {
40 src_tree: lhs,
41 recorder: crate::tree::Recorder::default().track_location(options.location),
42 visit: for_each,
43 location: options.location,
44 objects,
45 tracked: options.rewrites.map(rewrites::Tracker::new),
46 err: None,
47 };
48 match crate::tree(lhs, rhs, tree_diff_state, objects, &mut delegate) {
49 Ok(()) => {
50 let outcome = delegate.process_tracked_changes(resource_cache)?;
51 match delegate.err {
52 Some(err) => Err(callback_error(err)),
53 None => Ok(outcome),
54 }
55 }
56 Err(crate::tree::Error::Cancelled) => delegate
57 .err
58 .map_or(Err(Error::Cancelled), |err| Err(callback_error(err))),
59 Err(err) => Err(err),
60 }
61}
62
63struct Delegate<'a, 'old, VisitFn, 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<gix_error::Error>,
71}
72
73impl<VisitFn, Objects> Delegate<'_, '_, VisitFn, Objects>
74where
75 Objects: gix_object::FindObjectOrHeader,
76 VisitFn: FnMut(ChangeRef<'_>) -> Result<Action>,
77{
78 fn emit_change(
80 change: crate::tree::visit::Change,
81 location: &BStr,
82 visit: &mut VisitFn,
83 stored_err: &mut Option<gix_error::Error>,
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(std::ops::ControlFlow::Break(())) => std::ops::ControlFlow::Break(()),
122 Ok(std::ops::ControlFlow::Continue(())) => std::ops::ControlFlow::Continue(()),
123 Err(err) => {
124 *stored_err = Some(err);
125 std::ops::ControlFlow::Break(())
126 }
127 }
128 }
129
130 fn process_tracked_changes(
131 &mut self,
132 diff_cache: &mut crate::blob::Platform,
133 ) -> std::result::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(std::ops::ControlFlow::Break(())) => std::ops::ControlFlow::Break(()),
161 Ok(std::ops::ControlFlow::Continue(())) => std::ops::ControlFlow::Continue(()),
162 Err(err) => {
163 self.err = Some(err);
164 std::ops::ControlFlow::Break(())
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, Objects> crate::tree::Visit for Delegate<'_, '_, VisitFn, Objects>
183where
184 Objects: gix_object::FindObjectOrHeader,
185 VisitFn: FnMut(ChangeRef<'_>) -> Result<Action>,
186{
187 fn pop_front_tracked_path_and_set_current(&mut self) {
188 self.recorder.pop_front_tracked_path_and_set_current();
189 }
190
191 fn push_back_tracked_path_component(&mut self, component: &BStr) {
192 self.recorder.push_back_tracked_path_component(component);
193 }
194
195 fn push_path_component(&mut self, component: &BStr) {
196 self.recorder.push_path_component(component);
197 }
198
199 fn pop_path_component(&mut self) {
200 self.recorder.pop_path_component();
201 }
202
203 fn visit(&mut self, change: crate::tree::visit::Change) -> crate::tree::visit::Action {
204 match self.tracked.as_mut() {
205 Some(tracked) => tracked
206 .try_push_change(change, self.recorder.path())
207 .map_or(std::ops::ControlFlow::Continue(()), |change| {
208 Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err)
209 }),
210 None => Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err),
211 }
212 }
213}
214
215mod tree_to_changes {
216 use bstr::BStr;
217 use gix_object::tree::EntryRef;
218
219 use crate::tree::visit::Change;
220
221 pub struct Delegate<'a> {
222 push: &'a mut dyn FnMut(Change, &BStr),
223 recorder: gix_traverse::tree::Recorder,
224 }
225
226 impl<'a> Delegate<'a> {
227 pub fn new(push: &'a mut dyn FnMut(Change, &BStr), location: Option<crate::tree::recorder::Location>) -> Self {
228 let location = location.map(|t| match t {
229 crate::tree::recorder::Location::FileName => gix_traverse::tree::recorder::Location::FileName,
230 crate::tree::recorder::Location::Path => gix_traverse::tree::recorder::Location::Path,
231 });
232 Self {
233 push,
234 recorder: gix_traverse::tree::Recorder::default().track_location(location),
235 }
236 }
237 }
238
239 impl gix_traverse::tree::Visit for Delegate<'_> {
240 fn pop_back_tracked_path_and_set_current(&mut self) {
241 self.recorder.pop_back_tracked_path_and_set_current();
242 }
243
244 fn pop_front_tracked_path_and_set_current(&mut self) {
245 self.recorder.pop_front_tracked_path_and_set_current();
246 }
247
248 fn push_back_tracked_path_component(&mut self, component: &BStr) {
249 self.recorder.push_back_tracked_path_component(component);
250 }
251
252 fn push_path_component(&mut self, component: &BStr) {
253 self.recorder.push_path_component(component);
254 }
255
256 fn pop_path_component(&mut self) {
257 self.recorder.pop_path_component();
258 }
259
260 fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
261 std::ops::ControlFlow::Continue(true)
262 }
263
264 fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
265 if entry.mode.is_blob() {
266 (self.push)(
267 Change::Modification {
268 previous_entry_mode: entry.mode,
269 previous_oid: gix_hash::ObjectId::null(entry.oid.kind()),
270 entry_mode: entry.mode,
271 oid: entry.oid.to_owned(),
272 },
273 self.recorder.path(),
274 );
275 }
276 std::ops::ControlFlow::Continue(true)
277 }
278 }
279}