diff --git a/gix-object/src/tree/mod.rs b/gix-object/src/tree/mod.rs index 3bde58146cf..a6b59e436a7 100644 --- a/gix-object/src/tree/mod.rs +++ b/gix-object/src/tree/mod.rs @@ -12,6 +12,8 @@ mod ref_iter; /// pub mod write; +pub use ref_iter::iter_next; + /// The state needed to apply edits instantly to in-memory trees. /// /// It's made so that each tree is looked at in the object database at most once, and held in memory for diff --git a/gix-object/src/tree/ref_iter.rs b/gix-object/src/tree/ref_iter.rs index 7e60d6ac831..56da362d92d 100644 --- a/gix-object/src/tree/ref_iter.rs +++ b/gix-object/src/tree/ref_iter.rs @@ -1,8 +1,41 @@ +use std::ops::ControlFlow; + use bstr::BStr; use winnow::{error::ParserError, prelude::*}; use crate::{tree, tree::EntryRef, TreeRef, TreeRefIter}; +/// Yes +pub fn iter_next<'a, I, P>( + components: &mut core::iter::Peekable, + tree: crate::Data<'a>, +) -> core::ops::ControlFlow>, gix_hash::ObjectId> +where + I: Iterator, + P: PartialEq, +{ + if !tree.kind.is_tree() { + return ControlFlow::Break(None); + } + + let Some(component) = components.next() else { + return ControlFlow::Break(None); + }; + + let Some(entry) = TreeRefIter::from_bytes(tree.data) + .filter_map(Result::ok) + .find(|entry| component.eq(entry.filename)) + else { + return ControlFlow::Break(None); + }; + + if components.peek().is_none() { + ControlFlow::Break(Some(entry)) + } else { + ControlFlow::Continue(entry.oid.to_owned()) + } +} + impl<'a> TreeRefIter<'a> { /// Instantiate an iterator from the given tree data. pub fn from_bytes(data: &'a [u8]) -> TreeRefIter<'a> { @@ -28,30 +61,22 @@ impl<'a> TreeRefIter<'a> { P: PartialEq, { buffer.clear(); - - let mut path = path.into_iter().peekable(); buffer.extend_from_slice(self.data); - while let Some(component) = path.next() { - match TreeRefIter::from_bytes(buffer) - .filter_map(Result::ok) - .find(|entry| component.eq(entry.filename)) - { - Some(entry) => { - if path.peek().is_none() { - return Ok(Some(entry.into())); - } else { - let next_id = entry.oid.to_owned(); - let obj = odb.try_find(&next_id, buffer)?; - let Some(obj) = obj else { return Ok(None) }; - if !obj.kind.is_tree() { - return Ok(None); - } - } + + let mut iter = path.into_iter().peekable(); + let mut data = crate::Data::new(crate::Kind::Tree, buffer); + + loop { + data = match iter_next(&mut iter, data) { + ControlFlow::Continue(oid) => { + let Some(next_tree) = odb.try_find(&oid, buffer)? else { + break Ok(None); + }; + next_tree } - None => return Ok(None), + ControlFlow::Break(v) => break Ok(v.map(Into::into)), } } - Ok(None) } /// Like [`Self::lookup_entry()`], but takes any [`AsRef`](`std::path::Path`) directly via `relative_path`, diff --git a/gix/src/object/tree/mod.rs b/gix/src/object/tree/mod.rs index aa8a0aa253d..49151bb7a29 100644 --- a/gix/src/object/tree/mod.rs +++ b/gix/src/object/tree/mod.rs @@ -1,6 +1,8 @@ +use std::ops::ControlFlow; + use gix_hash::ObjectId; pub use gix_object::tree::{EntryKind, EntryMode}; -use gix_object::{bstr::BStr, FindExt, TreeRefIter}; +use gix_object::{bstr::BStr, tree::iter_next, FindExt, TreeRefIter}; use crate::{object::find, Id, ObjectDetached, Repository, Tree}; @@ -63,34 +65,25 @@ impl<'repo> Tree<'repo> { I: IntoIterator, P: PartialEq, { - let mut buf = self.repo.empty_reusable_buffer(); - buf.clear(); - - let mut path = path.into_iter().peekable(); + let buf = &mut self.repo.empty_reusable_buffer(); buf.extend_from_slice(&self.data); - while let Some(component) = path.next() { - match TreeRefIter::from_bytes(&buf) - .filter_map(Result::ok) - .find(|entry| component.eq(entry.filename)) - { - Some(entry) => { - if path.peek().is_none() { - return Ok(Some(Entry { - inner: entry.into(), - repo: self.repo, - })); - } else { - let next_id = entry.oid.to_owned(); - let obj = self.repo.objects.find(&next_id, &mut buf)?; - if !obj.kind.is_tree() { - return Ok(None); - } - } + + let mut iter = path.into_iter().peekable(); + let mut data = gix_object::Data::new(gix_object::Kind::Tree, buf); + + loop { + data = match iter_next(&mut iter, data) { + ControlFlow::Continue(oid) => self.repo.find(&oid, buf)?, + ControlFlow::Break(entry) => { + let mapped = entry.map(|e| Entry { + inner: e.into(), + repo: self.repo, + }); + + break Ok(mapped); } - None => return Ok(None), } } - Ok(None) } /// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component @@ -109,31 +102,22 @@ impl<'repo> Tree<'repo> { I: IntoIterator, P: PartialEq, { - let mut path = path.into_iter().peekable(); - while let Some(component) = path.next() { - match TreeRefIter::from_bytes(&self.data) - .filter_map(Result::ok) - .find(|entry| component.eq(entry.filename)) - { - Some(entry) => { - if path.peek().is_none() { - return Ok(Some(Entry { - inner: entry.into(), - repo: self.repo, - })); - } else { - let next_id = entry.oid.to_owned(); - let obj = self.repo.objects.find(&next_id, &mut self.data)?; - self.id = next_id; - if !obj.kind.is_tree() { - return Ok(None); - } - } + let mut iter = path.into_iter().peekable(); + let mut data = gix_object::Data::new(gix_object::Kind::Tree, &self.data); + + loop { + data = match iter_next(&mut iter, data) { + ControlFlow::Continue(oid) => { + self.id = oid; + self.repo.find(&oid, &mut self.data)? + } + ControlFlow::Break(entry) => { + let repo = self.repo; + let mapped = entry.map(|e| Entry { inner: e.into(), repo }); + break Ok(mapped); } - None => return Ok(None), } } - Ok(None) } /// Like [`Self::lookup_entry()`], but takes a `Path` directly via `relative_path`, a path relative to this tree.