blob: 90bbdbe3cee697adab79de4680c65e8662f1f468 [file] [log] [blame]
use std::path::Path;
use bstr::ByteSlice;
use gix_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<gix_attributes::Match<'_, ()>> {
let ignore = self.parent.state.ignore_or_panic();
let relative_path =
gix_path::to_unix_separators_on_windows(gix_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<gix_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),
}
}