gix_diff/tree/
recorder.rs

1use gix_hash::ObjectId;
2use gix_object::{
3    bstr::{BStr, BString, ByteSlice, ByteVec},
4    tree,
5};
6
7use crate::tree::visit::Relation;
8use crate::tree::{visit, Recorder, Visit};
9
10/// Describe how to track the location of a change.
11#[derive(Debug, Clone, Copy, Eq, PartialEq)]
12pub enum Location {
13    /// Track the entire path, relative to the repository.
14    Path,
15    /// Keep only the file-name as location, which may be enough for some calculations.
16    ///
17    /// This is less expensive than tracking the entire `Path`.
18    FileName,
19}
20
21/// A Change as observed by a call to [`visit(…)`](Visit::visit()), enhanced with the path affected by the change.
22/// Its similar to [`visit::Change`] but includes the path that changed.
23#[derive(Clone, Debug, PartialEq, Eq)]
24#[allow(missing_docs)]
25pub enum Change {
26    Addition {
27        entry_mode: tree::EntryMode,
28        oid: ObjectId,
29        path: BString,
30        relation: Option<Relation>,
31    },
32    Deletion {
33        entry_mode: tree::EntryMode,
34        oid: ObjectId,
35        path: BString,
36        relation: Option<Relation>,
37    },
38    Modification {
39        previous_entry_mode: tree::EntryMode,
40        previous_oid: ObjectId,
41
42        entry_mode: tree::EntryMode,
43        oid: ObjectId,
44
45        path: BString,
46    },
47}
48
49impl Default for Recorder {
50    fn default() -> Self {
51        Recorder {
52            path_deque: Default::default(),
53            path: Default::default(),
54            location: Some(Location::Path),
55            records: vec![],
56        }
57    }
58}
59
60/// Builder
61impl Recorder {
62    /// Obtain a copy of the currently tracked, full path of the entry.
63    pub fn track_location(mut self, location: Option<Location>) -> Self {
64        self.location = location;
65        self
66    }
67}
68
69/// Access
70impl Recorder {
71    /// Obtain a copy of the currently tracked, full path of the entry.
72    pub fn path_clone(&self) -> BString {
73        self.path.clone()
74    }
75
76    /// Return the currently set path.
77    pub fn path(&self) -> &BStr {
78        self.path.as_ref()
79    }
80}
81
82impl Recorder {
83    fn pop_element(&mut self) {
84        if let Some(pos) = self.path.rfind_byte(b'/') {
85            self.path.resize(pos, 0);
86        } else {
87            self.path.clear();
88        }
89    }
90
91    fn push_element(&mut self, name: &BStr) {
92        if name.is_empty() {
93            return;
94        }
95        if !self.path.is_empty() {
96            self.path.push(b'/');
97        }
98        self.path.push_str(name);
99    }
100}
101
102impl Visit for Recorder {
103    fn pop_front_tracked_path_and_set_current(&mut self) {
104        if let Some(Location::Path) = self.location {
105            self.path = self.path_deque.pop_front().expect("every parent is set only once");
106        }
107    }
108
109    fn push_back_tracked_path_component(&mut self, component: &BStr) {
110        match self.location {
111            None => {}
112            Some(Location::Path) => {
113                self.push_element(component);
114                self.path_deque.push_back(self.path.clone());
115            }
116            Some(Location::FileName) => {
117                self.path.clear();
118                self.path.extend_from_slice(component);
119            }
120        }
121    }
122
123    fn push_path_component(&mut self, component: &BStr) {
124        match self.location {
125            None => {}
126            Some(Location::Path) => {
127                self.push_element(component);
128            }
129            Some(Location::FileName) => {
130                self.path.clear();
131                self.path.extend_from_slice(component);
132            }
133        }
134    }
135
136    fn pop_path_component(&mut self) {
137        if let Some(Location::Path) = self.location {
138            self.pop_element();
139        }
140    }
141
142    fn visit(&mut self, change: visit::Change) -> visit::Action {
143        use visit::Change::*;
144        self.records.push(match change {
145            Deletion {
146                entry_mode,
147                oid,
148                relation,
149            } => Change::Deletion {
150                entry_mode,
151                oid,
152                path: self.path_clone(),
153                relation,
154            },
155            Addition {
156                entry_mode,
157                oid,
158                relation,
159            } => Change::Addition {
160                entry_mode,
161                oid,
162                path: self.path_clone(),
163                relation,
164            },
165            Modification {
166                previous_entry_mode,
167                previous_oid,
168                entry_mode,
169                oid,
170            } => Change::Modification {
171                previous_entry_mode,
172                previous_oid,
173                entry_mode,
174                oid,
175                path: self.path_clone(),
176            },
177        });
178        visit::Action::Continue
179    }
180}