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; #[derive(Default)] pub struct Paths { path_entries: Vec, paths_iter: Option>, iter: Option, } impl Paths { pub fn new(path_entries: Vec) -> Self { Self { path_entries, ..Default::default() } } } impl From<&Config> for Paths { fn from(value: &Config) -> Self { Paths { path_entries: value.paths.to_owned(), ..Default::default() } } } impl Iterator for Paths { type Item = PathBuf; 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 => match self.paths_iter.as_mut() { Some(paths_iter) => { let next = paths_iter.next()?; self.iter = Some( WalkBuilder::new(next.path) .standard_filters(true) .max_depth(next.recurse) .hidden(next.hidden) .build(), ); } None => self.paths_iter = Some(self.path_entries.to_owned().into_iter()), }, }; } } } #[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); } }