#[cfg(feature = "serde")]
use crate::path_buf::PathBuf;
#[cfg(not(feature = "serde"))]
use async_std::path::PathBuf;
use {async_std::path::Path, async_walkdir::WalkDir, futures::StreamExt};
pub type PathFilterRet = Result<bool, std::io::Error>;
pub type PathFilter = dyn Fn(&Path) -> PathFilterRet + Send + Sync;
pub fn create_combined_filter(
filters: Vec<Box<impl Fn(&Path) -> PathFilterRet + Send + Sync>>,
) -> impl Fn(&Path) -> PathFilterRet + Send + Sync {
move |file: &Path| {
for filter in &filters {
if !filter(file)? {
return Ok(false);
}
}
Ok(true)
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Supplied Input file is missing file name: '{0:#?}'")]
MissingFileName(PathBuf),
#[error("Failed to canonicalize (~- make absolute) path '{0:?}' with error: {1:?}")]
FailedToMakeAbsolute(PathBuf, std::io::Error),
#[error("I/O Error: '{0:#?}'")]
IO(#[from] std::io::Error),
}
pub async fn scan<F: Fn(&Path) -> PathFilterRet + Send + Sync, C: AsyncFnMut(PathBuf)>(
root: &Path,
filter: &F,
collector: &mut C,
) -> Result<(), Error> {
#[cfg(feature = "logging")]
log::debug!("Searching for files in directory '{root:?}' ...");
let mut dir_walker = WalkDir::new(root);
loop {
match dir_walker.next().await {
Some(Ok(entry)) => {
if let Ok(file_type) = entry.file_type().await
&& !file_type.is_dir()
{
add(filter, entry.path().as_ref(), collector).await?;
}
}
Some(Err(_err)) => (),
None => break,
}
}
Ok(())
}
pub async fn add<F: Fn(&Path) -> PathFilterRet + Send + Sync, C: AsyncFnMut(PathBuf)>(
filter: &F,
file: &Path,
collector: &mut C,
) -> Result<(), Error> {
if !filter(file)? {
return Ok(());
}
#[cfg(feature = "logging")]
log::debug!("Found file: '{file:?}'");
collector(file.into()).await;
Ok(())
}
pub async fn find<F: Fn(&Path) -> PathFilterRet + Send + Sync>(
root: &Path,
filter: &F,
) -> Result<Vec<PathBuf>, Error> {
let mut result = vec![];
let mut collector = async |file: PathBuf| result.push(file);
scan(root, &filter, &mut collector).await?;
Ok(result)
}
pub async fn find_root_stripped<F: Fn(&Path) -> PathFilterRet + Send + Sync>(
root: &Path,
filter: &F,
) -> Result<Vec<PathBuf>, Error> {
let mut result = vec![];
let mut collector =
async |file: PathBuf| result.push(file.strip_prefix(root).unwrap_or(file.as_path()).into());
scan(root, &filter, &mut collector).await?;
Ok(result)
}