use std::{ ops::{Deref, DerefMut}, path::{Path, PathBuf}, }; use ignore::WalkBuilder; pub struct Search { inner: WalkBuilder, paths: Vec, } impl Search { pub fn new>(path: P) -> Self { Self { inner: WalkBuilder::new(&path), paths: Vec::from([path.as_ref().to_owned()]), } } pub fn add>(&mut self, path: P) { self.paths.push(path.as_ref().to_owned()); self.inner.add(path); } pub fn build(mut self) -> impl Iterator { self.inner .standard_filters(true) .build() .flat_map(move |res| match res.map(|d| d.into_path()) { Ok(p) if self.paths.contains(&p) => { tracing::debug!(?p, "Ignoring search directory"); None } Ok(p) => Some(p), Err(err) => { tracing::error!(%err, "Ignoring errored path"); None } }) } } impl Deref for Search { type Target = WalkBuilder; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for Search { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl IntoIterator for Search { type Item = PathBuf; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.build().collect::>().into_iter() } } impl TryFrom for Search { type Error = (); fn try_from(value: crate::config::Search) -> Result { if value.paths.is_empty() { return Err(()); } let (init, paths) = value.paths.split_first().unwrap(); let mut search = Search::new(init); for path in paths { search.add(path); } search.max_depth(value.max_depth); search.hidden(!value.hidden); Ok(search) } }