summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 9dfa59c7b11db30cca63f871feda14f1559e8648 (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
130
131
132
133
use std::env;
use std::path::PathBuf;
use std::io;
use std::cmp::Ordering;

// Command line args are names of dirs, recursively list all files and
// dirs them
fn main() -> io::Result<()> {
    for dirname in env::args().skip(1) {
        let tree = DirTree::new(&dirname);
        for entry in tree {
            println!("{}", entry.path());
        }
    }
    Ok(())
}


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

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

    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)]
struct DirTree {
    entries: Vec<Entry>,
    dirs: Vec<Entry>,
}


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

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

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