summaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 76f0ccd7137428d3c1526143ad4a5aeae9ff555b (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
extern crate humansize;
extern crate ignore;
extern crate num_cpus;

use std::collections::HashSet;
use std::os::unix::fs::MetadataExt;
use std::sync::mpsc::channel;
use std::thread;

use humansize::{file_size_opts, FileSize};
use ignore::WalkBuilder;

fn main() {
    let mut builder = WalkBuilder::new("./");
    builder.hidden(false);
    builder.parents(false);
    builder.ignore(false);
    builder.git_global(false);
    builder.git_ignore(false);
    builder.git_exclude(false);
    builder.follow_links(false);

    builder.threads(num_cpus::get());

    let walker = builder.build_parallel();

    let (tx, rx) = channel();

    let receiver_thread = thread::spawn(move || {
        let mut total = 0;
        let mut ids = HashSet::new();
        for (unique_id, size) in rx {
            if let Some(unique_id) = unique_id {
                // Only count this entry if the ID has not been seen
                if ids.insert(unique_id) {
                    total += size;
                }
            } else {
                total += size;
            }
        }
        println!(
            "{} ({} bytes)",
            total.file_size(file_size_opts::DECIMAL).unwrap(),
            total
        );
    });

    walker.run(|| {
        let tx = tx.clone();
        Box::new(move |result| {
            match result {
                Ok(entry) => {
                    if let Ok(metadata) = entry.metadata() {
                        // If the entry has more than one hard link, generate
                        // a unique ID consisting of device and inode in order
                        // not to count this entry twice.
                        let unique_id = if metadata.is_file() && metadata.nlink() > 1 {
                            Some((metadata.dev(), metadata.ino()))
                        } else {
                            None
                        };

                        let size = metadata.len();

                        tx.send((unique_id, size)).ok();
                    } else {
                        eprintln!(
                            "Could not get metadata: '{}'",
                            entry.path().to_string_lossy()
                        );
                    }
                }
                Err(err) => {
                    eprintln!("I/O error: {}", err);
                }
            }

            return ignore::WalkState::Continue;
        })
    });

    drop(tx);
    receiver_thread.join().ok();
}