Skip to content

Commit a938fe4

Browse files
committed
refactor (#470)
1 parent 6ac7dbe commit a938fe4

File tree

4 files changed

+181
-176
lines changed

4 files changed

+181
-176
lines changed

git-repository/src/object/tree.rs

-176
This file was deleted.
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use super::Tree;
2+
use crate::Repository;
3+
4+
/// An entry within a tree
5+
pub struct EntryRef<'repo, 'a> {
6+
/// The actual entry ref we are wrapping.
7+
pub inner: git_object::tree::EntryRef<'a>,
8+
9+
repo: &'repo Repository,
10+
}
11+
12+
impl<'repo, 'a> EntryRef<'repo, 'a> {
13+
/// The kind of object to which [`id()`][Self::id()] is pointing.
14+
pub fn mode(&self) -> git_object::tree::EntryMode {
15+
self.inner.mode
16+
}
17+
18+
/// The name of the file in the parent tree.
19+
pub fn filename(&self) -> &git_object::bstr::BStr {
20+
self.inner.filename
21+
}
22+
23+
/// Return the entries id, connected to the underlying repository.
24+
pub fn id(&self) -> crate::Id<'repo> {
25+
crate::Id::from_id(self.inner.oid, self.repo)
26+
}
27+
}
28+
29+
impl<'repo, 'a> std::fmt::Display for EntryRef<'repo, 'a> {
30+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31+
write!(
32+
f,
33+
"{:06o} {:>6} {}\t{}",
34+
self.mode() as u32,
35+
self.mode().as_str(),
36+
self.id().shorten_or_id(),
37+
self.filename()
38+
)
39+
}
40+
}
41+
42+
impl<'repo> Tree<'repo> {
43+
/// Return an iterator over tree entries.
44+
pub fn iter(&self) -> impl Iterator<Item = Result<EntryRef<'repo, '_>, git_object::decode::Error>> {
45+
let repo = self.repo;
46+
git_object::TreeRefIter::from_bytes(&self.data).map(move |e| e.map(|entry| EntryRef { inner: entry, repo }))
47+
}
48+
}

git-repository/src/object/tree/mod.rs

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use git_hash::ObjectId;
2+
use git_object::{bstr::BStr, TreeRefIter};
3+
4+
use crate::{object::find, Id, Tree};
5+
6+
/// Initialization
7+
impl<'repo> Tree<'repo> {
8+
/// Obtain a tree instance by handing in all components that it is made up of.
9+
pub fn from_data(id: impl Into<ObjectId>, data: Vec<u8>, repo: &'repo crate::Repository) -> Self {
10+
Tree {
11+
id: id.into(),
12+
data,
13+
repo,
14+
}
15+
}
16+
}
17+
18+
/// Access
19+
impl<'repo> Tree<'repo> {
20+
/// Return this tree's identifier.
21+
pub fn id(&self) -> Id<'repo> {
22+
Id::from_id(self.id, self.repo)
23+
}
24+
25+
// TODO: move implementation to git-object, tests.
26+
/// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
27+
/// is looked up and its tree entry is returned.
28+
///
29+
/// # Performance Notes
30+
///
31+
/// Searching tree entries is currently done in sequence, which allows to the search to be allocation free. It would be possible
32+
/// to re-use a vector and use a binary search instead, which might be able to improve performance over all.
33+
/// However, a benchmark should be created first to have some data and see which trade-off to choose here.
34+
pub fn lookup_path<I, P>(mut self, path: I) -> Result<Option<git_object::tree::Entry>, find::existing::Error>
35+
where
36+
I: IntoIterator<Item = P>,
37+
P: PartialEq<BStr>,
38+
{
39+
let mut path = path.into_iter().peekable();
40+
while let Some(component) = path.next() {
41+
match TreeRefIter::from_bytes(&self.data)
42+
.filter_map(Result::ok)
43+
.find(|entry| component.eq(entry.filename))
44+
{
45+
Some(entry) => {
46+
if path.peek().is_none() {
47+
return Ok(Some(entry.into()));
48+
} else {
49+
let next_id = entry.oid.to_owned();
50+
let repo = self.repo;
51+
drop(self);
52+
self = match repo.find_object(next_id)?.try_into_tree() {
53+
Ok(tree) => tree,
54+
Err(_) => return Ok(None),
55+
};
56+
}
57+
}
58+
None => return Ok(None),
59+
}
60+
}
61+
Ok(None)
62+
}
63+
}
64+
65+
///
66+
pub mod traverse;
67+
68+
///
69+
mod iter;
70+
pub use iter::EntryRef;
71+
72+
impl<'r> std::fmt::Debug for Tree<'r> {
73+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74+
write!(f, "Tree({})", self.id)
75+
}
76+
}
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::Tree;
2+
use git_odb::FindExt;
3+
4+
/// Traversal
5+
impl<'repo> Tree<'repo> {
6+
/// Obtain a platform for initiating a variety of traversals.
7+
pub fn traverse(&self) -> Platform<'_, 'repo> {
8+
Platform {
9+
root: self,
10+
breadthfirst: BreadthFirstPresets { root: self },
11+
}
12+
}
13+
}
14+
15+
/// An intermediate object to start traversing the parent tree from.
16+
pub struct Platform<'a, 'repo> {
17+
root: &'a Tree<'repo>,
18+
/// TODO: EXPLAIN
19+
pub breadthfirst: BreadthFirstPresets<'a, 'repo>,
20+
}
21+
22+
/// TODO: explain THIS!
23+
#[derive(Copy, Clone)]
24+
pub struct BreadthFirstPresets<'a, 'repo> {
25+
root: &'a Tree<'repo>,
26+
}
27+
28+
impl<'a, 'repo> BreadthFirstPresets<'a, 'repo> {
29+
/// Returns all entries and their file paths, recursively, as reachable from this tree.
30+
pub fn files(&self) -> Result<Vec<git_traverse::tree::recorder::Entry>, git_traverse::tree::breadthfirst::Error> {
31+
let mut recorder = git_traverse::tree::Recorder::default();
32+
Platform {
33+
root: self.root,
34+
breadthfirst: *self,
35+
}
36+
.breadthfirst(&mut recorder)?;
37+
Ok(recorder.records)
38+
}
39+
}
40+
41+
impl<'a, 'repo> Platform<'a, 'repo> {
42+
/// Start a breadth-first traversal with a delegate, note that it's not sorted.
43+
/// TODO: more docs or links to git-traverse
44+
pub fn breadthfirst<V>(&self, delegate: &mut V) -> Result<(), git_traverse::tree::breadthfirst::Error>
45+
where
46+
V: git_traverse::tree::Visit,
47+
{
48+
let root = git_object::TreeRefIter::from_bytes(&self.root.data);
49+
let state = git_traverse::tree::breadthfirst::State::default();
50+
git_traverse::tree::breadthfirst(
51+
root,
52+
state,
53+
|oid, buf| self.root.repo.objects.find_tree_iter(oid, buf).ok(),
54+
delegate,
55+
)
56+
}
57+
}

0 commit comments

Comments
 (0)