summaryrefslogtreecommitdiff
path: root/src/fswalk.rs
blob: ac95540ec39a3da88e76da28545c906c5bd2e2ed (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
use std::io;
use std::path::PathBuf;
use std::cmp::Ordering;

// A filesystem entry: directory, file, symlink, etc
#[derive(Debug)]
pub struct Entry {
    path: PathBuf,
}

impl Entry {
    fn new(path: &PathBuf) -> Entry {
        Entry {
            path: path.clone(),
        }
    }

    pub fn path(&self) -> String {
        self.path.to_string_lossy().to_string()
    }
}

impl Eq for Entry {}

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        self.path == other.path
    }
}

impl Ord for Entry {
    fn cmp(&self, other: &Entry) -> Ordering {
        self.path.cmp(&other.path)
    }
}

impl PartialOrd for Entry {
    fn partial_cmp(&self, other: &Entry) -> Option<Ordering> {
        Some(self.path.cmp(&other.path))
    }
}

// Keep two stacks of Entry values, sorted in ascending order. The
// first stack has non-directories. When we iterate, we return a
// directory, then of its non-dirs, then recurse each of its subdirs.
#[derive(Debug)]
pub struct DirTree {
    entries: Vec<Entry>,
    dirs: Vec<Entry>,
}

impl DirTree {
    pub fn new(path: &str) -> DirTree {
        let mut dt = DirTree::empty();
        dt.add_dir(&PathBuf::from(path));
        dt
    }

    fn empty() -> DirTree {
        DirTree {
            entries: Vec::new(),
            dirs: Vec::new(),
        }
    }

    fn add_dir(&mut self, path: &PathBuf) {
        let entry = Entry::new(path);
        self.dirs.push(entry);
    }

    fn add_entries(&mut self, path: &PathBuf) -> io::Result<()> {
        let mut entries = Vec::new();
        let mut dirs = Vec::new();
        for dir_entry in path.read_dir()? {
            let dir_entry = dir_entry?;
            let metadata = dir_entry.metadata()?;
            let entry = Entry::new(&dir_entry.path());
            if metadata.is_dir() {
                dirs.push(entry);
            } else {
                entries.push(entry);
            }
        }

        entries.sort();
        entries.reverse();
        for e in entries {
            self.entries.push(e);
        }

        dirs.sort();
        dirs.reverse();
        for e in dirs {
            self.dirs.push(e);
        }

        Ok(())
    }
}

impl Iterator for DirTree {
    type Item = Entry;

    fn next(&mut self) -> Option<Entry> {
        if let Some(e) = self.entries.pop() {
            Some(e)
        } else if let Some(e) = self.dirs.pop() {
            if let Ok(_) = self.add_entries(&e.path) {
                // We ignore any errors from adding entries. Boo. Not
                // sure how I'd propagate errors, from a next method.
            }
            Some(e)
        } else {
            None
        }
    }
}