diff options
Diffstat (limited to 'vendor/gix-worktree/src/stack/delegate.rs')
-rw-r--r-- | vendor/gix-worktree/src/stack/delegate.rs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/vendor/gix-worktree/src/stack/delegate.rs b/vendor/gix-worktree/src/stack/delegate.rs new file mode 100644 index 000000000..28d8ecf34 --- /dev/null +++ b/vendor/gix-worktree/src/stack/delegate.rs @@ -0,0 +1,193 @@ +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) type FindFn<'a> = dyn for<'b> FnMut( + &gix_hash::oid, + &'b mut Vec<u8>, + ) -> Result<gix_object::BlobRef<'b>, Box<dyn std::error::Error + Send + Sync>> + + 'a; + +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 find: &'find mut FindFn<'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, .. } => { + attributes.push_directory( + stack.root(), + stack.current(), + rela_dir, + self.buf, + self.id_mappings, + self.find, + &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, + &mut self.find, + &mut self.statistics.attributes, + )?; + ignore.push_directory( + stack.root(), + stack.current(), + rela_dir, + self.buf, + self.id_mappings, + &mut self.find, + self.case, + &mut self.statistics.ignore, + )? + } + #[cfg(feature = "attributes")] + State::AttributesStack(attributes) => attributes.push_directory( + stack.root(), + stack.current(), + rela_dir, + self.buf, + self.id_mappings, + &mut self.find, + &mut self.statistics.attributes, + )?, + State::IgnoreStack(ignore) => ignore.push_directory( + stack.root(), + stack.current(), + rela_dir, + self.buf, + self.id_mappings, + &mut self.find, + 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, .. } => { + attributes.pop_directory(); + } + #[cfg(feature = "attributes")] + State::AttributesAndIgnoreStack { attributes, ignore } => { + attributes.pop_directory(); + ignore.pop_directory(); + } + #[cfg(feature = "attributes")] + State::AttributesStack(attributes) => { + attributes.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), + } +} |