mod entry;
mod filter;
mod input;
use crate::utils;
pub use entry::*;
pub use filter::*;
pub use input::*;
use std::iter;
use std::rc::Rc;
#[derive(Debug)]
pub struct Fetcher {
dirs: Vec<Entry>,
recurse: Recurse,
filter: FilterRules,
}
#[derive(Debug, Copy, Clone)]
pub enum TraversalMode {
Files,
DirsStop,
DirsAndContent,
ContentOverDirs,
}
#[derive(Debug, Copy, Clone)]
pub enum Recurse {
Full,
Shallow,
UpTo(u32),
}
impl Fetcher {
pub fn single(entry: &Entry, recurse: Recurse) -> Self {
Self::new(vec![entry.to_owned()], recurse, FilterRules::default())
}
pub fn new(dirs: Vec<Entry>, recurse: Recurse, filter: FilterRules) -> Self {
Fetcher {
dirs,
recurse,
filter,
}
}
pub fn fetch(self, mode: TraversalMode) -> impl Iterator<Item = Entry> {
let depth = self.recurse.into();
let fr = Rc::new(self.filter);
self.dirs
.into_iter()
.flat_map(move |dir| entries(dir, depth, mode, Rc::clone(&fr)))
}
}
fn entries(
dir: Entry,
depth: Depth,
mode: TraversalMode,
fr: Rc<FilterRules>,
) -> Box<dyn Iterator<Item = Entry>> {
if !utils::is_running() {
return Box::new(iter::empty());
}
match std::fs::read_dir(&dir) {
Ok(rd) => Box::new(
rd.inspect(|res| {
if let Err(err) = res {
eprintln!("error: dir entry: {err}");
}
})
.flatten()
.map(move |de| de.file_name().to_str().map(|s| dir.join(s)).ok_or(de))
.inspect(|res| {
if let Err(de) = res {
eprintln!("error: no UTF-8 name: {de:?}");
}
})
.flatten()
.flat_map(move |entry| {
use TraversalMode::*;
if !entry.is_dir() {
return if fr.is_in(&entry) && !entry.file_name().starts_with(".") {
Box::new(iter::once(entry)) as Box<dyn Iterator<Item = _>>
} else {
Box::new(iter::empty())
};
}
match (fr.is_in(&entry), (mode, depth.deeper())) {
(true, (DirsAndContent | ContentOverDirs, None) | (DirsStop, _)) => {
Box::new(iter::once(entry))
}
(true, (DirsAndContent, Some(d))) => Box::new(
iter::once(entry.clone()).chain(entries(entry, d, mode, Rc::clone(&fr))),
),
(_, (_, Some(d))) if !entry.file_name().starts_with(".") => {
entries(entry, d, mode, Rc::clone(&fr))
}
_ => Box::new(iter::empty()),
}
}),
),
Err(err) => {
eprintln!("error: read dir {dir}: {err}");
Box::new(iter::empty())
}
}
}
impl From<u32> for Recurse {
fn from(d: u32) -> Self {
match d {
0 => Recurse::Full,
1 => Recurse::Shallow,
_ => Recurse::UpTo(d),
}
}
}
impl From<Recurse> for Depth {
fn from(r: Recurse) -> Self {
match r {
Recurse::Full => Depth { max: 0, curr: 0 },
Recurse::Shallow => Depth { max: 1, curr: 0 },
Recurse::UpTo(d) => Depth { max: d, curr: 0 },
}
}
}
#[derive(Debug, Copy, Clone)]
struct Depth {
curr: u32,
max: u32,
}
impl Depth {
fn deeper(self) -> Option<Self> {
let Depth { curr, max } = self;
let curr = curr + 1;
(curr < max || max == 0).then_some(Depth { curr, max })
}
}