use std::collections::VecDeque;
use git_hash::ObjectId;
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("The tree {oid} could not be found")]
NotFound { oid: ObjectId },
#[error("The delegate cancelled the operation")]
Cancelled,
#[error(transparent)]
ObjectDecode(#[from] git_object::decode::Error),
}
#[derive(Default, Clone)]
pub struct State {
next: VecDeque<ObjectId>,
buf: Vec<u8>,
}
impl State {
fn clear(&mut self) {
self.next.clear();
self.buf.clear();
}
}
pub(crate) mod impl_ {
use std::borrow::BorrowMut;
use git_hash::oid;
use git_object::{tree::EntryMode, TreeRefIter};
use super::{Error, State};
use crate::tree::Visit;
pub fn traverse<StateMut, Find, V>(
root: TreeRefIter<'_>,
mut state: StateMut,
mut find: Find,
delegate: &mut V,
) -> Result<(), Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Option<TreeRefIter<'a>>,
StateMut: BorrowMut<State>,
V: Visit,
{
let state = state.borrow_mut();
state.clear();
let mut tree = root;
loop {
for entry in tree {
let entry = entry?;
match entry.mode {
EntryMode::Tree => {
use crate::tree::visit::Action::*;
delegate.push_path_component(entry.filename);
let action = delegate.visit_tree(&entry);
match action {
Skip => {}
Continue => {
delegate.pop_path_component();
delegate.push_back_tracked_path_component(entry.filename);
state.next.push_back(entry.oid.to_owned())
}
Cancel => {
return Err(Error::Cancelled);
}
}
}
_non_tree => {
delegate.push_path_component(entry.filename);
if delegate.visit_nontree(&entry).cancelled() {
return Err(Error::Cancelled);
}
}
}
delegate.pop_path_component();
}
match state.next.pop_front() {
Some(oid) => {
delegate.pop_front_tracked_path_and_set_current();
match find(&oid, &mut state.buf) {
Some(tree_iter) => tree = tree_iter,
None => return Err(Error::NotFound { oid: oid.to_owned() }),
}
}
None => break Ok(()),
}
}
}
}