use std::fs::File; use std::io::{BufRead, BufReader}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::{Instant, SystemTime}; use walkdir::WalkDir; pub struct IgnoreFile { globs: Arc<[Arc]>, } pub struct WorkFileMetadata { name: Arc, last_modified: Instant, } fn equal_to_path(a: impl AsRef, b: impl AsRef) -> bool { a.as_ref() == b.as_ref() } pub fn included_files( root: impl AsRef, ignored_files: Option<&IgnoreFile>, since: Option, ) -> std::io::Result> { let mut files = Vec::new(); let walker = WalkDir::new(root).into_iter().filter_entry(|entry| { !equal_to_path(entry.path(), ".pj") && !equal_to_path(entry.path(), ".ignore") && entry .metadata() .ok() .is_some_and(|metadata| metadata.is_file()) && ignored_files .is_none_or(|file| !file.should_ignore(entry.path().to_string_lossy().deref())) && since .zip(entry.metadata().ok()) .and_then(|(since, metadata)| { metadata.modified().ok().map(|modified| (since, modified)) }) .is_none_or(|(since, modified)| since < modified) }); for entry in walker { files.push(entry?.into_path()); } Ok(files) } impl IgnoreFile { fn open(root: impl AsRef) -> std::io::Result { let mut globs = Vec::new(); globs.push(String::from(".pj/")); let ignore_file = Path::join(root.as_ref(), ".ignore"); let ignore_file = BufReader::new(File::open(ignore_file)?); for line in ignore_file.lines() { globs.push(line?); } Ok(Self { globs: globs.iter().map(|s| s.deref().into()).collect(), }) } fn should_ignore(&self, path: &str) -> bool { self.globs .iter() .any(|glob| fast_glob::glob_match(&**glob, path)) } }