#![allow(missing_docs)] use std::path::{Path, PathBuf}; use bstr::{BStr, ByteSlice}; use git_hash::oid; use super::Cache; use crate::{fs, fs::PathOidMapping}; #[derive(Clone)] pub enum State { /// Useful for checkout where directories need creation, but we need to access attributes as well. CreateDirectoryAndAttributesStack { /// If there is a symlink or a file in our path, try to unlink it before creating the directory. unlink_on_collision: bool, /// just for testing #[cfg(debug_assertions)] test_mkdir_calls: usize, /// State to handle attribute information attributes: state::Attributes, }, /// Used when adding files, requiring access to both attributes and ignore information, for example during add operations. AttributesAndIgnoreStack { /// State to handle attribute information attributes: state::Attributes, /// State to handle exclusion information ignore: state::Ignore, }, /// Used when providing worktree status information. IgnoreStack(state::Ignore), } #[cfg(debug_assertions)] impl<'paths> Cache<'paths> { pub fn set_case(&mut self, case: git_glob::pattern::Case) { self.case = case; } pub fn num_mkdir_calls(&self) -> usize { match self.state { State::CreateDirectoryAndAttributesStack { test_mkdir_calls, .. } => test_mkdir_calls, _ => 0, } } pub fn reset_mkdir_calls(&mut self) { if let State::CreateDirectoryAndAttributesStack { test_mkdir_calls, .. } = &mut self.state { *test_mkdir_calls = 0; } } pub fn unlink_on_collision(&mut self, value: bool) { if let State::CreateDirectoryAndAttributesStack { unlink_on_collision, .. } = &mut self.state { *unlink_on_collision = value; } } } #[must_use] pub struct Platform<'a, 'paths> { parent: &'a Cache<'paths>, is_dir: Option, } impl<'paths> Cache<'paths> { /// Create a new instance with `worktree_root` being the base for all future paths we handle, assuming it to be valid which includes /// symbolic links to be included in it as well. /// The `case` configures attribute and exclusion query case sensitivity. pub fn new( worktree_root: impl Into, state: State, case: git_glob::pattern::Case, buf: Vec, attribute_files_in_index: Vec>, ) -> Self { let root = worktree_root.into(); Cache { stack: fs::Stack::new(root), state, case, buf, attribute_files_in_index, } } /// Append the `relative` path to the root directory the cache contains and efficiently create leading directories /// unless `is_dir` is known (`Some(…)`) then `relative` points to a directory itself in which case the entire resulting /// path is created as directory. If it's not known it is assumed to be a file. /// /// Provide access to cached information for that `relative` entry via the platform returned. pub fn at_path( &mut self, relative: impl AsRef, is_dir: Option, find: Find, ) -> std::io::Result> where Find: for<'a> FnMut(&oid, &'a mut Vec) -> Result, E>, E: std::error::Error + Send + Sync + 'static, { let mut delegate = platform::StackDelegate { state: &mut self.state, buf: &mut self.buf, is_dir: is_dir.unwrap_or(false), attribute_files_in_index: &self.attribute_files_in_index, find, }; self.stack.make_relative_path_current(relative, &mut delegate)?; Ok(Platform { parent: self, is_dir }) } /// **Panics** on illformed UTF8 in `relative` // TODO: more docs pub fn at_entry<'r, Find, E>( &mut self, relative: impl Into<&'r BStr>, is_dir: Option, find: Find, ) -> std::io::Result> where Find: for<'a> FnMut(&oid, &'a mut Vec) -> Result, E>, E: std::error::Error + Send + Sync + 'static, { let relative = relative.into(); let relative_path = git_path::from_bstr(relative); self.at_path( relative_path, is_dir.or_else(|| relative.ends_with_str("/").then(|| true)), // is_dir, find, ) } /// Return the base path against which all entries or paths should be relative to when querying. /// /// Note that this path _may_ not be canonicalized. pub fn base(&self) -> &Path { self.stack.root() } } mod platform; /// pub mod state;