gix_diff/tree/
recorder.rs1use 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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
12pub enum Location {
13 Path,
15 FileName,
19}
20
21#[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
60impl Recorder {
62 pub fn track_location(mut self, location: Option<Location>) -> Self {
64 self.location = location;
65 self
66 }
67}
68
69impl Recorder {
71 pub fn path_clone(&self) -> BString {
73 self.path.clone()
74 }
75
76 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}