summaryrefslogtreecommitdiffstats
path: root/vendor/gix-worktree/src/stack/delegate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-worktree/src/stack/delegate.rs')
-rw-r--r--vendor/gix-worktree/src/stack/delegate.rs193
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),
+ }
+}