use std::path::Path; use bstr::ByteSlice;use git_hash::oid; use crate::{ fs, fs::{ cache::{Platform, State}, PathOidMapping, },}; impl<'a> Platform<'a> { /// The full path to `relative` will be returned for use on the file system. pub fn path(&self) -> &'a Path { self.parent.stack.current() } /// See if the currently set entry is excluded as per exclude and git-ignore files. /// /// # Panics /// /// If the cache was configured without exclude patterns. pub fn is_excluded(&self) -> bool { self.matching_exclude_pattern() .map_or(false, |m| !m.pattern.is_negative()) } /// Check all exclude patterns to see if the currently set path matches any of them. /// /// Note that this pattern might be negated, and means this path in included. /// /// # Panics /// /// If the cache was configured without exclude patterns. pub fn matching_exclude_pattern(&self) -> Option<git_attributes::Match<'_, ()>> { let ignore = self.parent.state.ignore_or_panic(); let relative_path = git_path::to_unix_separators_on_windows(git_path::into_bstr(self.parent.stack.current_relative.as_path())); ignore.matching_exclude_pattern(relative_path.as_bstr(), self.is_dir, self.parent.case) }} impl<'a> std::fmt::Debug for Platform<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.path(), f) }} pub struct StackDelegate<'a, Find> { pub state: &'a mut State, pub buf: &'a mut Vec<u8>, pub is_dir: bool, pub attribute_files_in_index: &'a Vec<PathOidMapping>, pub find: Find,} impl<'a, Find, E> fs::stack::Delegate for StackDelegate<'a, Find>where Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<git_object::BlobRef<'b>, E>, E: std::error::Error + Send + Sync + 'static,{ fn push_directory(&mut self, stack: &fs::Stack) -> std::io::Result<()> { match &mut self.state { State::CreateDirectoryAndAttributesStack { attributes: _, .. } => { // TODO: attributes } State::AttributesAndIgnoreStack { ignore, attributes: _ } => { // TODO: attributes ignore.push_directory( &stack.root, &stack.current, self.buf, self.attribute_files_in_index, &mut self.find, )? } State::IgnoreStack(ignore) => ignore.push_directory( &stack.root, &stack.current, self.buf, self.attribute_files_in_index, &mut self.find, )?, } Ok(()) } fn push(&mut self, is_last_component: bool, stack: &fs::Stack) -> std::io::Result<()> { match &mut self.state { State::CreateDirectoryAndAttributesStack { #[cfg(debug_assertions)] test_mkdir_calls, unlink_on_collision, attributes: _, } => { #[cfg(debug_assertions)] { create_leading_directory( is_last_component, stack, self.is_dir, test_mkdir_calls, *unlink_on_collision, )? } #[cfg(not(debug_assertions))] { create_leading_directory(is_last_component, stack, self.is_dir, *unlink_on_collision)? } } State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) => {} } Ok(()) } fn pop_directory(&mut self) { match &mut self.state { State::CreateDirectoryAndAttributesStack { attributes: _, .. } => { // TODO: attributes } State::AttributesAndIgnoreStack { attributes: _, ignore } => { // TODO: attributes ignore.pop_directory(); } State::IgnoreStack(ignore) => { ignore.pop_directory(); } } }} fn create_leading_directory( is_last_component: bool, stack: &fs::Stack, is_dir: bool, #[cfg(debug_assertions)] mkdir_calls: &mut usize, unlink_on_collision: bool,) -> std::io::Result<()> { if is_last_component && !is_dir { return Ok(()); } #[cfg(debug_assertions)] { *mkdir_calls += 1; } match std::fs::create_dir(stack.current()) { Ok(()) => Ok(()), Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { let meta = stack.current().symlink_metadata()?; if meta.is_dir() { Ok(()) } else if unlink_on_collision { if meta.file_type().is_symlink() { crate::os::remove_symlink(stack.current())?; } else { std::fs::remove_file(stack.current())?; } #[cfg(debug_assertions)] { *mkdir_calls += 1; } std::fs::create_dir(stack.current()) } else { Err(err) } } Err(err) => Err(err), }}
use std::path::Path;
use bstr::ByteSlice;
use git_hash::oid;
use crate::{
fs,
fs::{
cache::{Platform, State},
PathOidMapping,
},
};
impl<'a> Platform<'a> {
/// The full path to `relative` will be returned for use on the file system.
pub fn path(&self) -> &'a Path {
self.parent.stack.current()
}
/// See if the currently set entry is excluded as per exclude and git-ignore files.
///
/// # Panics
///
/// If the cache was configured without exclude patterns.
pub fn is_excluded(&self) -> bool {
self.matching_exclude_pattern()
.map_or(false, |m| !m.pattern.is_negative())
}
/// Check all exclude patterns to see if the currently set path matches any of them.
///
/// Note that this pattern might be negated, and means this path in included.
///
/// # Panics
///
/// If the cache was configured without exclude patterns.
pub fn matching_exclude_pattern(&self) -> Option<git_attributes::Match<'_, ()>> {
let ignore = self.parent.state.ignore_or_panic();
let relative_path =
git_path::to_unix_separators_on_windows(git_path::into_bstr(self.parent.stack.current_relative.as_path()));
ignore.matching_exclude_pattern(relative_path.as_bstr(), self.is_dir, self.parent.case)
}
}
impl<'a> std::fmt::Debug for Platform<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.path(), f)
}
}
pub struct StackDelegate<'a, Find> {
pub state: &'a mut State,
pub buf: &'a mut Vec<u8>,
pub is_dir: bool,
pub attribute_files_in_index: &'a Vec<PathOidMapping>,
pub find: Find,
}
impl<'a, Find, E> fs::stack::Delegate for StackDelegate<'a, Find>
where
Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<git_object::BlobRef<'b>, E>,
E: std::error::Error + Send + Sync + 'static,
{
fn push_directory(&mut self, stack: &fs::Stack) -> std::io::Result<()> {
match &mut self.state {
State::CreateDirectoryAndAttributesStack { attributes: _, .. } => {
// TODO: attributes
}
State::AttributesAndIgnoreStack { ignore, attributes: _ } => {
// TODO: attributes
ignore.push_directory(
&stack.root,
&stack.current,
self.buf,
self.attribute_files_in_index,
&mut self.find,
)?
}
State::IgnoreStack(ignore) => ignore.push_directory(
&stack.root,
&stack.current,
self.buf,
self.attribute_files_in_index,
&mut self.find,
)?,
}
Ok(())
}
fn push(&mut self, is_last_component: bool, stack: &fs::Stack) -> std::io::Result<()> {
match &mut self.state {
State::CreateDirectoryAndAttributesStack {
#[cfg(debug_assertions)]
test_mkdir_calls,
unlink_on_collision,
attributes: _,
} => {
#[cfg(debug_assertions)]
{
create_leading_directory(
is_last_component,
stack,
self.is_dir,
test_mkdir_calls,
*unlink_on_collision,
)?
}
#[cfg(not(debug_assertions))]
{
create_leading_directory(is_last_component, stack, self.is_dir, *unlink_on_collision)?
}
}
State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) => {}
}
Ok(())
}
fn pop_directory(&mut self) {
match &mut self.state {
State::CreateDirectoryAndAttributesStack { attributes: _, .. } => {
// TODO: attributes
}
State::AttributesAndIgnoreStack { attributes: _, ignore } => {
// TODO: attributes
ignore.pop_directory();
}
State::IgnoreStack(ignore) => {
ignore.pop_directory();
}
}
}
}
fn create_leading_directory(
is_last_component: bool,
stack: &fs::Stack,
is_dir: bool,
#[cfg(debug_assertions)] mkdir_calls: &mut usize,
unlink_on_collision: bool,
) -> std::io::Result<()> {
if is_last_component && !is_dir {
return Ok(());
}
#[cfg(debug_assertions)]
{
*mkdir_calls += 1;
}
match std::fs::create_dir(stack.current()) {
Ok(()) => Ok(()),
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
let meta = stack.current().symlink_metadata()?;
if meta.is_dir() {
Ok(())
} else if unlink_on_collision {
if meta.file_type().is_symlink() {
crate::os::remove_symlink(stack.current())?;
} else {
std::fs::remove_file(stack.current())?;
}
#[cfg(debug_assertions)]
{
*mkdir_calls += 1;
}
std::fs::create_dir(stack.current())
} else {
Err(err)
}
}
Err(err) => Err(err),
}
}