liboskar/
types.rs

1extern crate pad;
2
3use self::pad::PadStr;
4use colored::*;
5use std::cmp::Ordering;
6use std::fmt;
7use std::path::PathBuf;
8
9/// This is just a wrapper around a `u64` so that we can implement our own `Display` trait for our
10/// file sizes.
11#[derive(Ord, Eq, PartialOrd, PartialEq, Copy, Clone)]
12pub struct FileSize {
13    size: u64,
14}
15
16impl FileSize {
17    pub fn new(i: u64) -> FileSize {
18        FileSize { size: i }
19    }
20
21    pub fn add(&mut self, other: FileSize) {
22        self.size += other.size;
23    }
24
25    pub fn get(self) -> u64 {
26        self.size
27    }
28}
29
30#[derive(Debug)]
31pub struct NamePair {
32    pub bytes: FileSize,
33    depth: u8,
34    pub name: String,
35    is_dir: bool,
36}
37
38fn sort_by_size(fst: &NamePair, snd: &NamePair) -> Ordering {
39    fst.bytes.cmp(&snd.bytes)
40}
41
42impl NamePair {
43    pub fn new(path: String, bytes_in: FileSize, d: u8, b: bool) -> NamePair {
44        NamePair {
45            name: path,
46            bytes: bytes_in,
47            depth: d,
48            is_dir: b,
49        }
50    }
51}
52
53pub struct FileTree {
54    pub file_size: FileSize,
55    files: Vec<NamePair>,
56}
57
58pub fn display_item(name: &str, bytes: FileSize) {
59    if bytes != FileSize::new(0) {
60        let to_formatted = format!("{}", bytes);
61        println!("{}\t {}", &to_formatted.green(), name);
62    }
63}
64
65impl Default for FileTree {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71impl FileTree {
72    pub fn sort(
73        mut self,
74        maybe_num: Option<usize>,
75        min_bytes: Option<u64>,
76        dirs_only: bool,
77        max_depth: Option<u8>,
78    ) -> FileTree {
79        let self_size = if Some(self.file_size) > min_bytes.map(FileSize::new) {
80            self.file_size
81        } else {
82            FileSize::new(0)
83        };
84
85        // filter by depth & truncate
86        if let Some(n) = maybe_num {
87            self.files.sort_by(|a, b| sort_by_size(b, a));
88            let new = self
89                .files
90                .into_iter()
91                .filter(|a| {
92                    (if dirs_only { a.is_dir } else { true })
93                        && Some(a.bytes) > min_bytes.map(FileSize::new)
94                        && (max_depth.is_none() || Some(a.depth) <= max_depth)
95                })
96                .take(n)
97                .collect::<Vec<NamePair>>();
98            FileTree {
99                file_size: self_size,
100                files: new,
101            }
102        }
103        // sort by size and filter by depth
104        else {
105            self.files.sort_by(|a, b| sort_by_size(a, b));
106            let new = self
107                .files
108                .into_iter()
109                .filter(|a| {
110                    (if dirs_only { a.is_dir } else { true })
111                        && Some(a.bytes) > min_bytes.map(FileSize::new)
112                        && (max_depth.is_none() || Some(a.depth) <= max_depth)
113                })
114                .collect::<Vec<NamePair>>();
115            FileTree {
116                file_size: self_size,
117                files: new,
118            }
119        }
120    }
121
122    pub fn filtered(
123        mut self,
124        min_bytes: Option<u64>,
125        dirs_only: bool,
126        max_depth: Option<u8>,
127    ) -> FileTree {
128        let self_size = if Some(self.file_size) > min_bytes.map(FileSize::new) {
129            self.file_size
130        } else {
131            FileSize::new(0)
132        };
133
134        self.files = self
135            .files
136            .into_iter()
137            .filter(|a| {
138                (if dirs_only { a.is_dir } else { true })
139                    && Some(a.bytes) > min_bytes.map(FileSize::new)
140                    && (max_depth.is_none() || Some(a.depth) <= max_depth)
141            })
142            .collect::<Vec<NamePair>>();
143
144        FileTree {
145            file_size: self_size,
146            files: self.files,
147        }
148    }
149
150    pub fn new() -> FileTree {
151        FileTree {
152            file_size: FileSize::new(0),
153            files: Vec::new(),
154        }
155    }
156
157    pub fn add(&mut self, size: FileSize) {
158        self.file_size.add(size);
159    }
160
161    pub fn push(
162        &mut self,
163        path: String,
164        size: FileSize,
165        subtree: Option<&mut FileTree>,
166        depth: u8,
167        is_dir: bool,
168    ) {
169        // add to total
170        self.file_size.add(size);
171
172        // append subtree if appropriate
173        if let Some(s) = subtree {
174            self.files.append(&mut s.files);
175        }
176
177        // return new file tree
178        self.files.push(NamePair::new(path, size, depth, is_dir));
179    }
180
181    pub fn display_tree(&mut self, init_dir: &PathBuf) {
182        // display stuff
183        let vec = &self.files;
184        for name_pair in vec {
185            if name_pair.bytes != FileSize::new(0) {
186                let to_formatted = format!("{}", name_pair.bytes);
187                println!("{}\t {}", &to_formatted.green(), name_pair.name);
188            }
189        }
190
191        if self.file_size != FileSize::new(0) {
192            let to_formatted = format!("{}", self.file_size);
193            let path = init_dir.display();
194            println!("{}\t {}", &to_formatted.green(), path);
195        }
196    }
197}
198
199impl fmt::Debug for FileSize {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        let pre_size = format!("{}", self.size);
202        write!(f, "{} b", &pre_size.pad_to_width(8))
203    }
204}
205
206impl fmt::Display for FileSize {
207    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208        if self.size < 1024 {
209            let pre_size = format!("{}", self.size);
210            write!(f, "{} b", &pre_size.pad_to_width(4))
211        } else if self.size < 1048576 {
212            let pre_size = if self.size / 1024 > 9 {
213                format!("{}", self.size / 1024)
214            } else if self.size as f32 / 1024.0 >= 9.95 {
215                format!("{:.0}", self.size as f32 / 1024.0)
216            } else {
217                format!("{:.1}", self.size as f32 / 1024.0)
218            };
219            write!(f, "{} kB", &pre_size.pad_to_width(4))
220        } else if self.size < 1073741824 {
221            // 2^30
222            let pre_size = if self.size / 1048576 > 9 {
223                format!("{}", self.size / 1048576)
224            } else if self.size as f32 / 1048576.0 >= 9.95 {
225                format!("{:.0}", self.size as f32 / 1048576.0)
226            } else {
227                format!("{:.1}", self.size as f32 / 1048576.0)
228            };
229            write!(f, "{} MB", &pre_size.pad_to_width(4))
230        } else if self.size < 1099511627776 {
231            // 2^40
232            let pre_size = if self.size / 1073741824 > 9 {
233                format!("{}", self.size / 1073741824)
234            } else if self.size as f32 / 1073741824.0 >= 9.95 {
235                format!("{:.0}", self.size as f32 / 1073741824.0)
236            } else {
237                format!("{:.1}", self.size as f32 / 1073741824.0)
238            };
239            write!(f, "{} GB", &pre_size.pad_to_width(4))
240        } else {
241            // for 1 TB and above
242            let pre_size = if self.size / 1099511627776 > 9 {
243                format!("{}", self.size / 1099511627776)
244            } else if self.size as f32 / 1099511627776.0 >= 9.95 {
245                format!("{:.0}", self.size as f32 / 1099511627776.0)
246            } else {
247                format!("{:.1}", self.size as f32 / 1099511627776.0)
248            };
249            write!(f, "{} TB", &pre_size.pad_to_width(4))
250        }
251    }
252}