use ignore::{Walk, WalkBuilder}; use std::{path::PathBuf, vec::IntoIter}; use tracing::warn; pub use config::{Config, PathEntry}; pub use error::Error; mod config; mod error; pub struct Paths { paths_iter: IntoIter, iter: Option, } impl Paths { pub fn new(path_entries: Vec) -> Self { Self { paths_iter: path_entries.into_iter(), iter: None, } } } impl From<&Config> for Paths { fn from(value: &Config) -> Self { Self::new(value.paths.to_owned()) } } impl Iterator for Paths { type Item = PathBuf; #[tracing::instrument(skip(self))] fn next(&mut self) -> Option { loop { match self.iter.as_mut().and_then(|iter| iter.next()) { Some(Ok(d)) => return Some(d.into_path()), Some(Err(err)) => warn!("{:?}", err), None => { let next = self.paths_iter.next()?; self.iter = Some( WalkBuilder::new(next.path) .standard_filters(true) .max_depth(next.recurse) .hidden(next.hidden) .build(), ); } }; } } } #[cfg(test)] mod tests { use std::fs; use super::*; #[test] fn test_iteration() { let test_dir = tempfile::Builder::new() .tempdir() .expect("Failed to create tmp directory"); let test_path = test_dir.path().to_owned(); let mut projects = Vec::from([ test_path.join("projects"), test_path.join("projects/project1"), test_path.join("projects/project2"), test_path.join("project3"), test_path.join("other_projects/project3"), ]); projects.iter().for_each(|project| { fs::create_dir_all(project).expect("Failed to create test project directory"); }); let directories = Paths::new(Vec::from([ PathEntry { path: test_path.join("projects"), hidden: false, recurse: Some(1), }, PathEntry { path: test_path.join("project3"), hidden: false, recurse: Some(0), }, PathEntry { path: test_path.join("other_projects/project3"), hidden: false, recurse: Some(0), }, ])); let mut actual = directories.into_iter().collect::>(); projects.sort(); actual.sort(); assert_eq!(projects, actual); } }