summaryrefslogtreecommitdiffstats
path: root/src/common.rs
blob: 86e5ac4701935744a66a3dee56bc57403b143b60 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
use jwalk::WalkDir;
use std::fmt;
use std::path::Path;

/// Specifies a way to format bytes
#[derive(Clone, Copy)]
pub enum ByteFormat {
    /// metric format, based on 1000.
    Metric,
    /// binary format, based on 1024
    Binary,
    /// raw bytes, without additional formatting
    Bytes,
}

impl ByteFormat {
    pub fn display(&self, bytes: u64) -> ByteFormatDisplay {
        ByteFormatDisplay {
            format: *self,
            bytes,
        }
    }
}

pub struct ByteFormatDisplay {
    format: ByteFormat,
    bytes: u64,
}

impl fmt::Display for ByteFormatDisplay {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use byte_unit::Byte;
        use ByteFormat::*;

        let binary = match self.format {
            Bytes => return write!(f, "{} b", self.bytes),
            Binary => true,
            Metric => false,
        };
        let b = Byte::from_bytes(self.bytes as u128)
            .get_appropriate_unit(binary)
            .format(2);
        let mut splits = b.split(' ');
        match (splits.next(), splits.next()) {
            (Some(bytes), Some(unit)) => write!(
                f,
                "{:>8} {:>unit_width$}",
                bytes,
                unit,
                unit_width = match self.format {
                    Binary => 3,
                    Metric => 2,
                    _ => 2,
                }
            ),
            _ => f.write_str(&b),
        }
    }
}

/// Identify the kind of sorting to apply during filesystem iteration
#[derive(Clone)]
pub enum TraversalSorting {
    None,
    AlphabeticalByFileName,
}

/// 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.
#[derive(Clone)]
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,
    pub sorting: TraversalSorting,
}

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

/// 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,
}