173 lines · 6.0 KB · Rust
RawDownload
use bstr::{BStr, ByteSlice}; use crate::{stack::State, PathIdMapping}; /// Various aggregate numbers related to the stack delegate itself.#[derive(Default, Clone, Copy, Debug)]#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]pub struct Statistics {    /// The amount of `std::fs::create_dir` calls.    ///    /// This only happens if we are in the respective mode to create leading directories efficiently.    pub num_mkdir_calls: usize,    /// Amount of calls to push a path element.    pub push_element: usize,    /// Amount of calls to push a directory.    pub push_directory: usize,    /// Amount of calls to pop a directory.    pub pop_directory: usize,} pub(crate) struct StackDelegate<'a, 'find> {    pub state: &'a mut State,    pub buf: &'a mut Vec<u8>,    #[cfg_attr(not(feature = "attributes"), allow(dead_code))]    pub is_dir: bool,    pub id_mappings: &'a Vec<PathIdMapping>,    pub objects: &'find dyn gix_object::Find,    pub case: gix_glob::pattern::Case,    pub statistics: &'a mut super::Statistics,} impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> {    fn push_directory(&mut self, stack: &gix_fs::Stack) -> std::io::Result<()> {        self.statistics.delegate.push_directory += 1;        let dir_bstr = gix_path::into_bstr(stack.current());        let rela_dir_cow = gix_path::to_unix_separators_on_windows(            gix_glob::search::pattern::strip_base_handle_recompute_basename_pos(                gix_path::into_bstr(stack.root()).as_ref(),                dir_bstr.as_ref(),                None,                self.case,            )            .expect("dir in root")            .0,        );        let rela_dir: &BStr = if rela_dir_cow.starts_with(b"/") {            rela_dir_cow[1..].as_bstr()        } else {            rela_dir_cow.as_ref()        };        match &mut self.state {            #[cfg(feature = "attributes")]            State::CreateDirectoryAndAttributesStack { attributes, .. } | State::AttributesStack(attributes) => {                attributes.push_directory(                    stack.root(),                    stack.current(),                    rela_dir,                    self.buf,                    self.id_mappings,                    self.objects,                    &mut self.statistics.attributes,                )?;            }            #[cfg(feature = "attributes")]            State::AttributesAndIgnoreStack { ignore, attributes } => {                attributes.push_directory(                    stack.root(),                    stack.current(),                    rela_dir,                    self.buf,                    self.id_mappings,                    self.objects,                    &mut self.statistics.attributes,                )?;                ignore.push_directory(                    stack.root(),                    stack.current(),                    rela_dir,                    self.buf,                    self.id_mappings,                    self.objects,                    self.case,                    &mut self.statistics.ignore,                )?            }            State::IgnoreStack(ignore) => ignore.push_directory(                stack.root(),                stack.current(),                rela_dir,                self.buf,                self.id_mappings,                self.objects,                self.case,                &mut self.statistics.ignore,            )?,        }        Ok(())    }     #[cfg_attr(not(feature = "attributes"), allow(unused_variables))]    fn push(&mut self, is_last_component: bool, stack: &gix_fs::Stack) -> std::io::Result<()> {        self.statistics.delegate.push_element += 1;        match &mut self.state {            #[cfg(feature = "attributes")]            State::CreateDirectoryAndAttributesStack {                unlink_on_collision,                attributes: _,            } => create_leading_directory(                is_last_component,                stack,                self.is_dir,                &mut self.statistics.delegate.num_mkdir_calls,                *unlink_on_collision,            )?,            #[cfg(feature = "attributes")]            State::AttributesAndIgnoreStack { .. } | State::AttributesStack(_) => {}            State::IgnoreStack(_) => {}        }        Ok(())    }     fn pop_directory(&mut self) {        self.statistics.delegate.pop_directory += 1;        match &mut self.state {            #[cfg(feature = "attributes")]            State::CreateDirectoryAndAttributesStack { attributes, .. } | State::AttributesStack(attributes) => {                attributes.pop_directory();            }            #[cfg(feature = "attributes")]            State::AttributesAndIgnoreStack { attributes, ignore } => {                attributes.pop_directory();                ignore.pop_directory();            }            State::IgnoreStack(ignore) => {                ignore.pop_directory();            }        }    }} #[cfg(feature = "attributes")]fn create_leading_directory(    is_last_component: bool,    stack: &gix_fs::Stack,    is_dir: bool,    mkdir_calls: &mut usize,    unlink_on_collision: bool,) -> std::io::Result<()> {    if is_last_component && !is_dir {        return Ok(());    }    *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() {                    gix_fs::symlink::remove(stack.current())?;                } else {                    std::fs::remove_file(stack.current())?;                }                *mkdir_calls += 1;                std::fs::create_dir(stack.current())            } else {                Err(err)            }        }        Err(err) => Err(err),    }}