From a7caf8a1c89300a478bc4f90b0f68ec1da6661ad Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Thu, 17 Aug 2023 18:45:57 -0400 Subject: [PATCH] Exclude files using glob patterns --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/events.rs | 13 +++++++++++++ src/file.rs | 29 +++++++++++++++++++++++++++-- src/main.rs | 11 ++++++++++- 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index faf8ac4..dd278dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/Cargo.toml b/Cargo.toml index 1616d7c..50486f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/events.rs b/src/events.rs index bdf9132..4ce1eff 100644 --- a/src/events.rs +++ b/src/events.rs @@ -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 for WatchEventType { fn from(e: DebouncedEvent) -> WatchEventType { match e { diff --git a/src/file.rs b/src/file.rs index 9918dfc..1adcf46 100644 --- a/src/file.rs +++ b/src/file.rs @@ -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>, files: Vec<&'a str>, + exclude: Option>, method: WatchEventType, poll: Option, } @@ -36,6 +38,7 @@ impl<'a> FileProcess<'a> { cmd: &'a str, env: Option>, file: Vec<&'a str>, + exclude: Option>, method: WatchEventType, poll: Option, ) -> 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>, ) -> 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(()) } } diff --git a/src/main.rs b/src/main.rs index ec0a5f9..cd9623f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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.