dir_iterator/lib.rs
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
//! Directory Iterator
#![allow(dead_code)]
mod filter;
#[cfg(test)]
mod test;
use std::*;
/// We deal with std::io::Error
type Result<T> = io::Result<T>;
/// scan a directory recursively and access with iterator
pub struct DirIterator {
/// current subdirectory (last is current folder)
stack: Vec<fs::ReadDir>,
/// configuration
config: DirIteratorConfig,
}
impl DirIterator {
/// Return an iterator builder aiming on current directory
pub fn current() -> DirIteratorBuilder {
Self::from_path(env::current_dir().expect("could not retrieve current dir"))
}
/// Scan current directory and return result as iterator
pub fn build_current() -> impl Iterator<Item = fs::DirEntry> {
Self::current().build().expect("path not found")
}
/// Return an iterator builder aiming on given directory
pub fn from_path(path: impl AsRef<path::Path>) -> DirIteratorBuilder {
DirIteratorBuilder {
path: path.as_ref().to_path_buf(),
..Default::default()
}
}
/// Scan given `path`` and return result as iterator
pub fn build_from_path(
path: impl AsRef<path::Path>,
) -> Result<impl Iterator<Item = fs::DirEntry>> {
Self::from_path(path).build()
}
/// Create from `DirIteratorBuilder`
fn from_builder(builder: DirIteratorBuilder) -> Result<Self> {
Ok(Self {
stack: vec![fs::read_dir(builder.path)?],
config: builder.config,
})
}
}
impl Iterator for DirIterator {
type Item = Result<fs::DirEntry>;
fn next(&mut self) -> Option<Self::Item> {
loop {
// get current `read_dir()` result on the stack
if let Some(it) = self.stack.last_mut() {
// get next item
match it.next() {
// got one
Some(Ok(item)) => {
// check file type
match item.file_type() {
Ok(file_type) => {
// ignore folders if configured
if self.config.ignore.iter().any(|ignore| {
ignore.is_match(item.file_name().as_encoded_bytes())
}) {
continue;
}
// Push new item on stack when the file entry is a directory
if file_type.is_dir() {
match fs::read_dir(item.path()) {
Ok(dir_entry) => self.stack.push(dir_entry),
Err(err) => return Some(Err(err)),
}
}
// return next item
return Some(Ok(item));
}
// report error in item
Err(err) => return Some(Err(err)),
}
}
None => {
// finished with current `read_dir()` result
self.stack.pop()?;
}
err => return err,
}
} else {
return None;
}
}
}
}
/// Configuration of a `DirIterator`
#[derive(Default)]
struct DirIteratorConfig {
/// If set do not scan directories which file name matches this wildcard
ignore: Vec<wc::Wildcard<'static>>,
}
/// Builder for configuration of `DirIterator`
#[derive(Default)]
pub struct DirIteratorBuilder {
/// Path to scan
path: path::PathBuf,
/// Scanner configuration
config: DirIteratorConfig,
}
impl DirIteratorBuilder {
/// finish configuration and build a `DirIterator`
pub fn build(self) -> Result<impl Iterator<Item = fs::DirEntry>> {
Ok(DirIterator::from_builder(self)?.flatten())
}
/// configures to ignore folders by wildcard
pub fn ignore(mut self, wildcard: &'static str) -> Self {
self.config
.ignore
.push(wc::Wildcard::new(wildcard.as_bytes()).expect("misformed wildcard"));
self
}
}