git_diff/tree/
recorder.rs

1use std::collections::VecDeque;
2
3use git_hash::ObjectId;
4use git_object::{
5    bstr::{BStr, BString, ByteSlice, ByteVec},
6    tree,
7};
8
9use crate::tree::visit;
10
11/// A Change as observed by a call to [`visit(…)`][visit::Visit::visit()], enhanced with the path affected by the change.
12/// Its similar to [visit::Change] but includes the path that changed.
13#[derive(Clone, Debug, PartialEq, Eq)]
14#[allow(missing_docs)]
15pub enum Change {
16    Addition {
17        entry_mode: tree::EntryMode,
18        oid: ObjectId,
19        path: BString,
20    },
21    Deletion {
22        entry_mode: tree::EntryMode,
23        oid: ObjectId,
24        path: BString,
25    },
26    Modification {
27        previous_entry_mode: tree::EntryMode,
28        previous_oid: ObjectId,
29
30        entry_mode: tree::EntryMode,
31        oid: ObjectId,
32
33        path: BString,
34    },
35}
36
37/// A [Visit][visit::Visit] implementation to record every observed change and keep track of the changed paths.
38#[derive(Clone, Debug, Default)]
39pub struct Recorder {
40    path_deque: VecDeque<BString>,
41    path: BString,
42    /// The observed changes.
43    pub records: Vec<Change>,
44}
45
46impl Recorder {
47    fn pop_element(&mut self) {
48        if let Some(pos) = self.path.rfind_byte(b'/') {
49            self.path.resize(pos, 0);
50        } else {
51            self.path.clear();
52        }
53    }
54
55    fn push_element(&mut self, name: &BStr) {
56        if !self.path.is_empty() {
57            self.path.push(b'/');
58        }
59        self.path.push_str(name);
60    }
61
62    fn path_clone(&self) -> BString {
63        self.path.clone()
64    }
65}
66
67impl visit::Visit for Recorder {
68    fn pop_front_tracked_path_and_set_current(&mut self) {
69        self.path = self.path_deque.pop_front().expect("every parent is set only once");
70    }
71
72    fn push_back_tracked_path_component(&mut self, component: &BStr) {
73        self.push_element(component);
74        self.path_deque.push_back(self.path.clone());
75    }
76
77    fn push_path_component(&mut self, component: &BStr) {
78        self.push_element(component);
79    }
80
81    fn pop_path_component(&mut self) {
82        self.pop_element();
83    }
84
85    fn visit(&mut self, change: visit::Change) -> visit::Action {
86        use visit::Change::*;
87        self.records.push(match change {
88            Deletion { entry_mode, oid } => Change::Deletion {
89                entry_mode,
90                oid,
91                path: self.path_clone(),
92            },
93            Addition { entry_mode, oid } => Change::Addition {
94                entry_mode,
95                oid,
96                path: self.path_clone(),
97            },
98            Modification {
99                previous_entry_mode,
100                previous_oid,
101                entry_mode,
102                oid,
103            } => Change::Modification {
104                previous_entry_mode,
105                previous_oid,
106                entry_mode,
107                oid,
108                path: self.path_clone(),
109            },
110        });
111        visit::Action::Continue
112    }
113}