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, visit::Relation, Recorder, Visit};
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq)]
11pub enum Location {
12 Path,
14 FileName,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
23#[allow(missing_docs)]
24pub enum Change {
25 Addition {
26 entry_mode: tree::EntryMode,
27 oid: ObjectId,
28 path: BString,
29 relation: Option<Relation>,
30 },
31 Deletion {
32 entry_mode: tree::EntryMode,
33 oid: ObjectId,
34 path: BString,
35 relation: Option<Relation>,
36 },
37 Modification {
38 previous_entry_mode: tree::EntryMode,
39 previous_oid: ObjectId,
40
41 entry_mode: tree::EntryMode,
42 oid: ObjectId,
43
44 path: BString,
45 },
46}
47
48impl Default for Recorder {
49 fn default() -> Self {
50 Recorder {
51 path_deque: Default::default(),
52 path: Default::default(),
53 location: Some(Location::Path),
54 records: vec![],
55 }
56 }
57}
58
59impl Recorder {
61 pub fn track_location(mut self, location: Option<Location>) -> Self {
63 self.location = location;
64 self
65 }
66}
67
68impl Recorder {
70 pub fn path_clone(&self) -> BString {
72 self.path.clone()
73 }
74
75 pub fn path(&self) -> &BStr {
77 self.path.as_ref()
78 }
79}
80
81impl Recorder {
82 fn pop_element(&mut self) {
83 if let Some(pos) = self.path.rfind_byte(b'/') {
84 self.path.resize(pos, 0);
85 } else {
86 self.path.clear();
87 }
88 }
89
90 fn push_element(&mut self, name: &BStr) {
91 if name.is_empty() {
92 return;
93 }
94 if !self.path.is_empty() {
95 self.path.push(b'/');
96 }
97 self.path.push_str(name);
98 }
99}
100
101impl Visit for Recorder {
102 fn pop_front_tracked_path_and_set_current(&mut self) {
103 if let Some(Location::Path) = self.location {
104 self.path = self.path_deque.pop_front().expect("every parent is set only once");
105 }
106 }
107
108 fn push_back_tracked_path_component(&mut self, component: &BStr) {
109 match self.location {
110 None => {}
111 Some(Location::Path) => {
112 self.push_element(component);
113 self.path_deque.push_back(self.path.clone());
114 }
115 Some(Location::FileName) => {
116 self.path.clear();
117 self.path.extend_from_slice(component);
118 }
119 }
120 }
121
122 fn push_path_component(&mut self, component: &BStr) {
123 match self.location {
124 None => {}
125 Some(Location::Path) => {
126 self.push_element(component);
127 }
128 Some(Location::FileName) => {
129 self.path.clear();
130 self.path.extend_from_slice(component);
131 }
132 }
133 }
134
135 fn pop_path_component(&mut self) {
136 if let Some(Location::Path) = self.location {
137 self.pop_element();
138 }
139 }
140
141 fn visit(&mut self, change: visit::Change) -> visit::Action {
142 use visit::Change::*;
143 self.records.push(match change {
144 Deletion {
145 entry_mode,
146 oid,
147 relation,
148 } => Change::Deletion {
149 entry_mode,
150 oid,
151 path: self.path_clone(),
152 relation,
153 },
154 Addition {
155 entry_mode,
156 oid,
157 relation,
158 } => Change::Addition {
159 entry_mode,
160 oid,
161 path: self.path_clone(),
162 relation,
163 },
164 Modification {
165 previous_entry_mode,
166 previous_oid,
167 entry_mode,
168 oid,
169 } => Change::Modification {
170 previous_entry_mode,
171 previous_oid,
172 entry_mode,
173 oid,
174 path: self.path_clone(),
175 },
176 });
177 visit::Action::Continue
178 }
179}