gix_diff/tree/
visit.rs

1use gix_hash::ObjectId;
2use gix_object::{tree, tree::EntryMode};
3
4/// A way to recognize and associate different [`Change`] instances.
5///
6/// These are unique only within one diff operation.
7pub type ChangeId = u32;
8
9/// Identifies a relationship between this instance and another one.
10#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
11pub enum Relation {
12    /// This is a parent with the given ID, which will always have at least one child
13    /// assuming that empty directories are not allowed in valid trees.
14    /// It's also always a tree which is the start of a recursive deletion or addition.
15    ///
16    /// The change with this relation is always emitted first.
17    Parent(ChangeId),
18    /// This is a direct or indirect child, tree or not tree, of the parent with the given ID.
19    ChildOfParent(ChangeId),
20}
21
22/// Represents any possible change in order to turn one tree into another.
23#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
24pub enum Change {
25    /// An entry was added, like the addition of a file or directory.
26    Addition {
27        /// The mode of the added entry.
28        entry_mode: tree::EntryMode,
29        /// The object id of the added entry.
30        oid: ObjectId,
31        /// Possibly associate this change with another for hierarchical rename tracking.
32        relation: Option<Relation>,
33    },
34    /// An entry was deleted, like the deletion of a file or directory.
35    Deletion {
36        /// The mode of the deleted entry.
37        entry_mode: tree::EntryMode,
38        /// The object id of the deleted entry.
39        oid: ObjectId,
40        /// Possibly associate this change with another for hierarchical rename tracking.
41        relation: Option<Relation>,
42    },
43    /// An entry was modified, e.g. changing the contents of a file adjusts its object id and turning
44    /// a file into a symbolic link adjusts its mode.
45    Modification {
46        /// The mode of the entry before the modification.
47        previous_entry_mode: tree::EntryMode,
48        /// The object id of the entry before the modification.
49        previous_oid: ObjectId,
50
51        /// The mode of the entry after the modification.
52        entry_mode: tree::EntryMode,
53        /// The object id after the modification.
54        oid: ObjectId,
55    },
56}
57
58impl Change {
59    /// Return the current object id.
60    pub fn oid(&self) -> &gix_hash::oid {
61        match self {
62            Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
63        }
64    }
65    /// Return the current tree entry mode.
66    pub fn entry_mode(&self) -> EntryMode {
67        match self {
68            Change::Addition { entry_mode, .. }
69            | Change::Deletion { entry_mode, .. }
70            | Change::Modification { entry_mode, .. } => *entry_mode,
71        }
72    }
73    /// Return the current object id and tree entry mode of a change.
74    pub fn oid_and_entry_mode(&self) -> (&gix_hash::oid, EntryMode) {
75        match self {
76            Change::Addition {
77                oid,
78                entry_mode,
79                relation: _,
80            }
81            | Change::Deletion {
82                oid,
83                entry_mode,
84                relation: _,
85            }
86            | Change::Modification { oid, entry_mode, .. } => (oid, *entry_mode),
87        }
88    }
89}
90
91/// What to do after a [Change] was [recorded](super::Visit::visit()).
92///
93/// Use [`std::ops::ControlFlow::Continue`] to continue the traversal of changes.
94/// Use [`std::ops::ControlFlow::Break`] to stop the traversal of changes, making this the last call to [visit(…)](super::Visit::visit()).
95pub type Action = std::ops::ControlFlow<()>;
96
97#[cfg(feature = "blob")]
98mod change_impls {
99    use gix_hash::oid;
100    use gix_object::tree::EntryMode;
101
102    use crate::{
103        rewrites::tracker::ChangeKind,
104        tree::visit::{Change, Relation},
105    };
106
107    impl crate::rewrites::tracker::Change for crate::tree::visit::Change {
108        fn id(&self) -> &oid {
109            match self {
110                Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
111            }
112        }
113
114        fn relation(&self) -> Option<Relation> {
115            match self {
116                Change::Addition { relation, .. } | Change::Deletion { relation, .. } => *relation,
117                Change::Modification { .. } => None,
118            }
119        }
120
121        fn kind(&self) -> ChangeKind {
122            match self {
123                Change::Addition { .. } => ChangeKind::Addition,
124                Change::Deletion { .. } => ChangeKind::Deletion,
125                Change::Modification { .. } => ChangeKind::Modification,
126            }
127        }
128
129        fn entry_mode(&self) -> EntryMode {
130            match self {
131                Change::Addition { entry_mode, .. }
132                | Change::Deletion { entry_mode, .. }
133                | Change::Modification { entry_mode, .. } => *entry_mode,
134            }
135        }
136
137        fn id_and_entry_mode(&self) -> (&oid, EntryMode) {
138            match self {
139                Change::Addition { entry_mode, oid, .. }
140                | Change::Deletion { entry_mode, oid, .. }
141                | Change::Modification { entry_mode, oid, .. } => (oid, *entry_mode),
142            }
143        }
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn size_of_change() {
153        let actual = std::mem::size_of::<Change>();
154        assert!(
155            actual <= 48,
156            "{actual} <= 48: this type shouldn't grow without us knowing"
157        );
158    }
159}