1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use jwalk::WalkDir;
use std::fmt;
use std::path::Path;

/// Specifies a way to format bytes
pub enum ByteFormat {
    /// metric format, based on 1000.
    Metric,
    /// binary format, based on 1024
    Binary,
    /// raw bytes, without additional formatting
    Bytes,
}

pub(crate) enum Sorting {
    None,
}

/// Specify the kind of color to use
#[derive(Clone, Copy)]
pub enum Color {
    /// Use no color
    None,
    /// Use terminal colors
    Terminal,
}

pub(crate) struct DisplayColor<C> {
    kind: Color,
    color: C,
}

impl Color {
    pub(crate) fn display<C>(&self, color: C) -> DisplayColor<C> {
        DisplayColor { kind: *self, color }
    }
}

impl<C> fmt::Display for DisplayColor<C>
where
    C: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.kind {
            Color::None => Ok(()),
            Color::Terminal => self.color.fmt(f),
        }
    }
}

/// Configures a filesystem walk, including output and formatting options.
pub struct WalkOptions {
    /// The amount of threads to use. Refer to [`WalkDir::num_threads()`](https://docs.rs/jwalk/0.4.0/jwalk/struct.WalkDir.html#method.num_threads)
    /// for more information.
    pub threads: usize,
    pub byte_format: ByteFormat,
    pub color: Color,
}

impl WalkOptions {
    pub(crate) fn format_bytes(&self, b: u64) -> String {
        use byte_unit::Byte;
        use ByteFormat::*;
        let binary = match self.byte_format {
            Bytes => return format!("{} b", b),
            Binary => true,
            Metric => false,
        };
        let b = Byte::from_bytes(b as u128)
            .get_appropriate_unit(binary)
            .format(2);
        let mut splits = b.split(' ');
        match (splits.next(), splits.next()) {
            (Some(bytes), Some(unit)) => format!(
                "{:>8} {:>unit_width$}",
                bytes,
                unit,
                unit_width = match self.byte_format {
                    Binary => 3,
                    Metric => 2,
                    _ => 2,
                }
            ),
            _ => b,
        }
    }

    pub(crate) fn iter_from_path(&self, path: &Path, sort: Sorting) -> WalkDir {
        WalkDir::new(path)
            .preload_metadata(true)
            .sort(match sort {
                Sorting::None => false,
            })
            .skip_hidden(false)
            .num_threads(self.threads)
    }
}

/// Statistics obtained during a filesystem walk
#[derive(Default, Debug)]
pub struct Statistics {
    /// The amount of files we have seen
    pub files_traversed: u64,
    /// The size of the smallest file encountered in bytes
    pub smallest_file_in_bytes: u64,
    /// The size of the largest file encountered in bytes
    pub largest_file_in_bytes: u64,
}

/// Information we gather during a filesystem walk
#[derive(Default)]
pub struct WalkResult {
    /// The amount of io::errors we encountered. Can happen when fetching meta-data, or when reading the directory contents.
    pub num_errors: u64,
    pub stats: Statistics,
}