Exclude files using glob patterns

This commit is contained in:
Jeremy Wall 2023-08-17 18:45:57 -04:00
parent 38b0b6aa59
commit a7caf8a1c8
5 changed files with 58 additions and 3 deletions

7
Cargo.lock generated
View File

@ -109,6 +109,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -298,6 +304,7 @@ name = "runwhen"
version = "0.0.7"
dependencies = [
"clap",
"glob",
"humantime",
"notify",
]

View File

@ -11,6 +11,7 @@ license = "Apache-2.0"
[dependencies]
humantime = "2.1.0"
notify = "4.0.17"
glob = "0.3.1"
[dependencies.clap]
version = "3.2.17"

View File

@ -21,6 +21,19 @@ pub enum WatchEventType {
Ignore,
}
pub fn get_file(evt: &DebouncedEvent) -> Option<&std::path::PathBuf> {
match evt {
DebouncedEvent::NoticeWrite(b)
| DebouncedEvent::NoticeRemove(b)
| DebouncedEvent::Create(b)
| DebouncedEvent::Write(b)
| DebouncedEvent::Chmod(b)
| DebouncedEvent::Remove(b)
| DebouncedEvent::Rename(b, _) => Some(b),
DebouncedEvent::Error(_, _) | DebouncedEvent::Rescan => None,
}
}
impl From<DebouncedEvent> for WatchEventType {
fn from(e: DebouncedEvent) -> WatchEventType {
match e {

View File

@ -16,6 +16,7 @@ use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::time::{Duration, Instant};
use glob;
use notify::{watcher, RecursiveMode, Watcher};
use error::CommandError;
@ -27,6 +28,7 @@ pub struct FileProcess<'a> {
cmd: &'a str,
env: Option<Vec<String>>,
files: Vec<&'a str>,
exclude: Option<Vec<&'a str>>,
method: WatchEventType,
poll: Option<Duration>,
}
@ -36,6 +38,7 @@ impl<'a> FileProcess<'a> {
cmd: &'a str,
env: Option<Vec<String>>,
file: Vec<&'a str>,
exclude: Option<Vec<&'a str>>,
method: WatchEventType,
poll: Option<Duration>,
) -> FileProcess<'a> {
@ -44,6 +47,7 @@ impl<'a> FileProcess<'a> {
env,
method,
poll,
exclude,
files: file,
}
}
@ -122,6 +126,7 @@ fn wait_for_fs_events(
ch: Sender<()>,
method: WatchEventType,
files: &Vec<&str>,
excluded: &Option<Vec<&str>>,
) -> Result<(), CommandError> {
// Notify requires a channel for communication.
let (tx, rx) = channel();
@ -137,9 +142,29 @@ fn wait_for_fs_events(
watcher.watch(*file, RecursiveMode::Recursive)?;
println!("Watching {:?}", *file);
}
let mut patterns = Vec::new();
if let Some(exclude) = excluded {
for ef in exclude.iter() {
patterns.push(glob::Pattern::new(*ef).expect("Invalid path pattern"));
//patterns.push(ef.clone());
println!("Added pattern {:?}", patterns.iter().last());
}
}
loop {
let evt: WatchEventType = match rx.recv() {
Ok(event) => WatchEventType::from(event),
Ok(event) => {
// TODO(jwall): Filter this based on the exclude pattern
if let Some(f) = crate::events::get_file(&event) {
for pat in patterns.iter() {
println!("Testing pattern {:?} against {:?}", pat, f);
if pat.matches_path(&f) {
println!("Excluding: {:?}", f);
continue;
}
}
}
WatchEventType::from(event)
}
Err(e) => {
println!("Watch Error: {}", e);
WatchEventType::Error
@ -175,7 +200,7 @@ impl<'a> Process for FileProcess<'a> {
watch_for_change_events(rx, cmd, env, poll);
}
});
wait_for_fs_events(tx, self.method.clone(), &self.files)?;
wait_for_fs_events(tx, self.method.clone(), &self.files, &self.exclude)?;
Ok(())
}
}

View File

@ -16,6 +16,7 @@
extern crate clap;
extern crate humantime;
extern crate notify;
extern crate glob;
use std::{process, str::FromStr};
@ -47,6 +48,10 @@ fn do_flags() -> clap::ArgMatches {
arg!(-f --file).name("file")
.takes_value(true).help("File or directory to watch for changes"),
)
.arg(
arg!(-e --exclude).name("exclude")
.takes_value(true).help("path names to skip when watching. Specified in unix glob format."),
)
.arg(arg!(--touch).name("filetouch").help("Use file or directory timestamps to monitor for changes."))
.arg(arg!(--poll).name("poll").takes_value(true).value_parser(value_parser!(humantime::Duration)).help("Duration of time between polls")))
.subcommand(
@ -90,8 +95,12 @@ fn main() {
Some(d) => Some((*d).into()),
None => None,
};
let exclude = match matches.values_of("exclude") {
Some(vr) => Some(vr.collect()),
None => None,
};
println!("Enforcing a poll time of {:?}", duration);
Box::new(FileProcess::new(cmd, maybe_env, file, method, duration))
Box::new(FileProcess::new(cmd, maybe_env, file, exclude, method, duration))
} else if let Some(matches) = app.subcommand_matches("timer") {
// TODO(jwall): This should use cancelable commands.
// Unwrap because this flag is required.