Sfoglia il codice sorgente

refactor: Add RecursiveSize type to simplify total-size calculation

Robert Minsk 2 anni fa
parent
commit
6ea282f6c9
4 ha cambiato i file con 133 aggiunte e 43 eliminazioni
  1. 56 42
      src/fs/file.rs
  2. 1 0
      src/fs/mod.rs
  3. 75 0
      src/fs/recursive_size.rs
  4. 1 1
      src/output/details.rs

+ 56 - 42
src/fs/file.rs

@@ -22,6 +22,7 @@ use crate::fs::dir::Dir;
 use crate::fs::feature::xattr;
 use crate::fs::feature::xattr::{Attribute, FileAttributes};
 use crate::fs::fields as f;
+use crate::fs::recursive_size::RecursiveSize;
 
 use super::mounts::all_mounts;
 use super::mounts::MountedFs;
@@ -39,7 +40,6 @@ static DIRECTORY_SIZE_CACHE: Lazy<Mutex<HashMap<(u64, u64), u64>>> =
 /// once, have its file extension extracted at least once, and have its metadata
 /// information queried at least once, so it makes sense to do all this at the
 /// start and hold on to all the information.
-#[allow(clippy::struct_excessive_bools)]
 pub struct File<'dir> {
     /// The filename portion of this file’s path, including the extension.
     ///
@@ -92,16 +92,14 @@ pub struct File<'dir> {
     /// instead.
     pub deref_links: bool,
 
+    /// The recursive directory size when total_size is used.
+    recursive_size: RecursiveSize,
+
     /// The extended attributes of this file.
     extended_attributes: OnceLock<Vec<Attribute>>,
 
     /// The absolute value of this path, used to look up mount points.
     absolute_path: OnceLock<Option<PathBuf>>,
-
-    pub total_size: bool,
-
-    /// The recursive directory size when total_size is used.
-    recursive_size: Option<u64>,
 }
 
 impl<'dir> File<'dir> {
@@ -125,6 +123,11 @@ impl<'dir> File<'dir> {
         let is_all_all = false;
         let extended_attributes = OnceLock::new();
         let absolute_path = OnceLock::new();
+        let recursive_size = if total_size {
+            RecursiveSize::Unknown
+        } else {
+            RecursiveSize::None
+        };
 
         let mut file = File {
             name,
@@ -136,12 +139,11 @@ impl<'dir> File<'dir> {
             deref_links,
             extended_attributes,
             absolute_path,
-            total_size,
-            recursive_size: None,
+            recursive_size,
         };
 
         if total_size {
-            file.recursive_size = file.total_directory_size();
+            file.recursive_size = file.recursive_directory_size();
         }
 
         Ok(file)
@@ -161,6 +163,11 @@ impl<'dir> File<'dir> {
         let parent_dir = Some(parent_dir);
         let extended_attributes = OnceLock::new();
         let absolute_path = OnceLock::new();
+        let recursive_size = if total_size {
+            RecursiveSize::Unknown
+        } else {
+            RecursiveSize::None
+        };
 
         let mut file = File {
             name: name.into(),
@@ -172,12 +179,11 @@ impl<'dir> File<'dir> {
             deref_links: false,
             extended_attributes,
             absolute_path,
-            total_size,
-            recursive_size: None,
+            recursive_size,
         };
 
         if total_size {
-            file.recursive_size = file.total_directory_size();
+            file.recursive_size = file.recursive_directory_size();
         }
 
         Ok(file)
@@ -403,8 +409,7 @@ impl<'dir> File<'dir> {
                     deref_links: self.deref_links,
                     extended_attributes,
                     absolute_path: absolute_path_cell,
-                    total_size: false,
-                    recursive_size: None,
+                    recursive_size: RecursiveSize::None,
                 };
                 FileTarget::Ok(Box::new(file))
             }
@@ -545,31 +550,46 @@ impl<'dir> File<'dir> {
         }
     }
 
+    /// Returns the size of the file or indicates no size if it's a directory.
+    ///
+    /// For Windows platforms, the size of directories is not computed and will
+    /// return `Size::None`.
+    #[cfg(windows)]
+    pub fn size(&self) -> f::Size {
+        if self.is_directory() {
+            f::Size::None
+        } else {
+            f::Size::Some(self.metadata.len())
+        }
+    }
+
+
     /// Calculate the total directory size recursively.  If not a directory `None`
     /// will be returned.  The directory size is cached for recursive directory
     /// listing.
     #[cfg(unix)]
-    fn total_directory_size(&self) -> Option<u64> {
+    fn recursive_directory_size(&self) -> RecursiveSize {
         if self.is_directory() {
             let key = (self.metadata.dev(), self.metadata.ino());
             if let Some(size) = DIRECTORY_SIZE_CACHE.lock().unwrap().get(&key) {
-                return Some(*size);
+                return RecursiveSize::Some(*size);
             }
-            let dir = Dir::read_dir(self.path.clone()).ok()?;
-            let mut size = 0;
-            for file in dir
-                .files(super::DotFilter::Dotfiles, None, false, false, true)
-                .flatten()
-            {
-                size += match file.total_directory_size() {
-                    Some(s) => s,
-                    _ => file.metadata.size(),
+            Dir::read_dir(self.path.clone()).map_or(RecursiveSize::Unknown, |dir| {
+                let mut size = 0;
+                for file in dir
+                    .files(super::DotFilter::Dotfiles, None, false, false, true)
+                    .flatten()
+                {
+                    size += match file.recursive_directory_size() {
+                        RecursiveSize::Some(s) => s,
+                        _ => file.metadata.size(),
+                    }
                 }
-            }
-            DIRECTORY_SIZE_CACHE.lock().unwrap().insert(key, size);
-            Some(size)
+                DIRECTORY_SIZE_CACHE.lock().unwrap().insert(key, size);
+                RecursiveSize::Some(size)
+            })
         } else {
-            None
+            RecursiveSize::None
         }
     }
 
@@ -578,27 +598,21 @@ impl<'dir> File<'dir> {
     /// not cache the sizes.  Without caching we could end up walking the
     /// directory structure several times.
     #[cfg(windows)]
-    fn total_directory_size(&self) -> Option<u64> {
-        None
+    fn recursive_directory_size(&self) -> RecursiveSize {
+        RecursiveSize::None
     }
 
     /// Returns the same value as `self.metadata.len()` or the recursive size
     /// of a directory when `total_size` is used.
+    #[inline]
     pub fn length(&self) -> u64 {
         self.recursive_size.unwrap_or(self.metadata.len())
     }
 
-    /// Returns the size of the file or indicates no size if it's a directory.
-    ///
-    /// For Windows platforms, the size of directories is not computed and will
-    /// return `Size::None`.
-    #[cfg(windows)]
-    pub fn size(&self) -> f::Size {
-        if self.is_directory() {
-            f::Size::None
-        } else {
-            f::Size::Some(self.metadata.len())
-        }
+    /// Is the file is using recursive size calculation
+    #[inline]
+    pub fn is_recursive_size(&self) -> bool {
+        ! self.recursive_size.is_none()
     }
 
     /// Determines if the directory is empty or not.

+ 1 - 0
src/fs/mod.rs

@@ -9,3 +9,4 @@ pub mod feature;
 pub mod fields;
 pub mod filter;
 pub mod mounts;
+pub mod recursive_size;

+ 75 - 0
src/fs/recursive_size.rs

@@ -0,0 +1,75 @@
+/// Used to represent a the size of a recursive directory traversal.  `None`
+/// should be used when the file does not represent a directory or the recursive
+/// size should not be calculated.
+#[derive(Copy, Clone, Debug)]
+pub enum RecursiveSize {
+    /// Size should not be computed
+    None,
+    /// Size should be computed but has not been computed yet
+    Unknown,
+    /// Size has been computed
+    Some(u64),
+}
+
+impl RecursiveSize {
+    /// Returns `true` if `None`
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use eza::fs::recursive_size::RecursiveSize;
+    ///
+    /// let x = RecursiveSize::None;
+    /// assert_eq!(x.is_none(), true);
+    ///
+    /// let x = RecursiveSize::Unknown;
+    /// assert_eq!(x.is_none(), false);
+    ///
+    /// let x = RecursiveSize::Some(0);
+    /// assert_eq!(x.is_none(), false);
+    #[inline]
+    pub const fn is_none(&self) -> bool {
+        matches!(*self, Self::None)
+    }
+
+    /// Returns the contained [`Some`] value or a provided default.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use eza::fs::recursive_size::RecursiveSize;
+    ///
+    /// assert_eq!(RecursiveSize::None.unwrap_or(1), 1);
+    /// assert_eq!(RecursiveSize::Unknown.unwrap_or(1), 1);
+    /// assert_eq!(RecursiveSize::Some(2).unwrap_or(1), 2);
+    /// ```
+    #[inline]
+    pub const fn unwrap_or(self, default: u64) -> u64 {
+        match self {
+            Self::Some(x) => x,
+            _ => default,
+        }
+    }
+
+    /// Returns the provided default result (if None or Unknown),
+    /// or applies a function to the contained value (if Some).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use eza::fs::recursive_size::RecursiveSize;
+    ///
+    /// assert_eq!(RecursiveSize::None.map_or(None, |s| Some(s * 2)), None);
+    /// assert_eq!(RecursiveSize::Unknown.map_or(None, |s| Some(s * 2)), None);
+    /// assert_eq!(RecursiveSize::Some(2).map_or(None, |s| Some(s * 2)), Some(4));
+    #[inline]
+    pub fn map_or<U, F>(self, default: U, f: F) -> U
+    where
+        F: FnOnce(u64) -> U,
+    {
+        match self {
+            RecursiveSize::Some(x) => f(x),
+            _ => default,
+        }
+    }
+}

+ 1 - 1
src/output/details.rs

@@ -347,7 +347,7 @@ impl<'a> Render<'a> {
                     self.git,
                     self.git_ignoring,
                     egg.file.deref_links,
-                    egg.file.total_size,
+                    egg.file.is_recursive_size(),
                 ) {
                     match file_to_add {
                         Ok(f) => {