gix_traverse/tree/
recorder.rs

1use gix_hash::ObjectId;
2use gix_object::{
3    bstr::{BStr, BString, ByteSlice, ByteVec},
4    tree,
5};
6
7use crate::tree::{visit::Action, Recorder, Visit};
8
9/// Describe how to track the location of an entry.
10#[derive(Debug, Clone, Copy, Eq, PartialEq)]
11pub enum Location {
12    /// Track the entire path, relative to the repository.
13    Path,
14    /// Keep only the file-name as location, which may be enough for some calculations.
15    ///
16    /// This is less expensive than tracking the entire `Path`.
17    FileName,
18}
19
20/// An owned entry as observed by a call to [`visit_(tree|nontree)(…)`][Visit::visit_tree()], enhanced with the full path to it.
21/// Otherwise similar to [`gix_object::tree::EntryRef`].
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct Entry {
24    /// The kind of entry, similar to entries in a unix directory tree.
25    pub mode: tree::EntryMode,
26    /// The full path to the entry. A root entry would be `d`, and a file `a` within the directory would be `d/a`.
27    ///
28    /// This is independent of the platform and the path separators actually used there.
29    pub filepath: BString,
30    /// The id of the entry which can be used to locate it in an object database.
31    pub oid: ObjectId,
32}
33
34impl Entry {
35    fn new(entry: &tree::EntryRef<'_>, filepath: BString) -> Self {
36        Entry {
37            filepath,
38            oid: entry.oid.to_owned(),
39            mode: entry.mode,
40        }
41    }
42}
43
44impl Default for Recorder {
45    fn default() -> Self {
46        Recorder {
47            path_deque: Default::default(),
48            path: Default::default(),
49            location: Location::Path.into(),
50            records: vec![],
51        }
52    }
53}
54
55impl Recorder {
56    fn pop_element(&mut self) {
57        if let Some(pos) = self.path.rfind_byte(b'/') {
58            self.path.resize(pos, 0);
59        } else {
60            self.path.clear();
61        }
62    }
63
64    fn push_element(&mut self, name: &BStr) {
65        if name.is_empty() {
66            return;
67        }
68        if !self.path.is_empty() {
69            self.path.push(b'/');
70        }
71        self.path.push_str(name);
72    }
73}
74
75/// Builder
76impl Recorder {
77    /// Obtain a copy of the currently tracked, full path of the entry.
78    pub fn track_location(mut self, location: Option<Location>) -> Self {
79        self.location = location;
80        self
81    }
82}
83
84/// Access
85impl Recorder {
86    /// Obtain a copy of the currently tracked, full path of the entry.
87    pub fn path_clone(&self) -> BString {
88        self.path.clone()
89    }
90
91    /// Return the currently set path.
92    pub fn path(&self) -> &BStr {
93        self.path.as_ref()
94    }
95}
96
97impl Visit for Recorder {
98    fn pop_back_tracked_path_and_set_current(&mut self) {
99        if let Some(Location::Path) = self.location {
100            self.path = self.path_deque.pop_back().unwrap_or_default();
101        }
102    }
103
104    fn pop_front_tracked_path_and_set_current(&mut self) {
105        if let Some(Location::Path) = self.location {
106            self.path = self
107                .path_deque
108                .pop_front()
109                .expect("every call is matched with push_tracked_path_component");
110        }
111    }
112
113    fn push_back_tracked_path_component(&mut self, component: &BStr) {
114        if let Some(Location::Path) = self.location {
115            self.push_element(component);
116            self.path_deque.push_back(self.path.clone());
117        }
118    }
119
120    fn push_path_component(&mut self, component: &BStr) {
121        match self.location {
122            None => {}
123            Some(Location::Path) => {
124                self.push_element(component);
125            }
126            Some(Location::FileName) => {
127                self.path.clear();
128                self.path.extend_from_slice(component);
129            }
130        }
131    }
132
133    fn pop_path_component(&mut self) {
134        if let Some(Location::Path) = self.location {
135            self.pop_element();
136        }
137    }
138
139    fn visit_tree(&mut self, entry: &tree::EntryRef<'_>) -> Action {
140        self.records.push(Entry::new(entry, self.path_clone()));
141        Action::Continue
142    }
143
144    fn visit_nontree(&mut self, entry: &tree::EntryRef<'_>) -> Action {
145        self.records.push(Entry::new(entry, self.path_clone()));
146        Action::Continue
147    }
148}