#![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 Cache { 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> { parent: &'a Cache, is_dir: Option<bool>,} impl Cache { /// 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<PathBuf>, state: State, case: git_glob::pattern::Case, buf: Vec<u8>, attribute_files_in_index: Vec<PathOidMapping>, ) -> 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<Find, E>( &mut self, relative: impl AsRef<Path>, is_dir: Option<bool>, find: Find, ) -> std::io::Result<Platform<'_>> where Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, 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<bool>, find: Find, ) -> std::io::Result<Platform<'_>> where Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, 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_some(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;
#![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 Cache {
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> {
parent: &'a Cache,
is_dir: Option<bool>,
}
impl Cache {
/// 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<PathBuf>,
state: State,
case: git_glob::pattern::Case,
buf: Vec<u8>,
attribute_files_in_index: Vec<PathOidMapping>,
) -> 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<Find, E>(
&mut self,
relative: impl AsRef<Path>,
is_dir: Option<bool>,
find: Find,
) -> std::io::Result<Platform<'_>>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, 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<bool>,
find: Find,
) -> std::io::Result<Platform<'_>>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, 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_some(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;