mirror of
https://github.com/zaphar/runwhen.git
synced 2025-07-23 20:49:49 -04:00
Exclude files using glob patterns
This commit is contained in:
parent
38b0b6aa59
commit
a7caf8a1c8
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -109,6 +109,12 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -298,6 +304,7 @@ name = "runwhen"
|
|||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"glob",
|
||||||
"humantime",
|
"humantime",
|
||||||
"notify",
|
"notify",
|
||||||
]
|
]
|
||||||
|
@ -11,6 +11,7 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
|
glob = "0.3.1"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "3.2.17"
|
version = "3.2.17"
|
||||||
|
@ -21,6 +21,19 @@ pub enum WatchEventType {
|
|||||||
Ignore,
|
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 {
|
impl From<DebouncedEvent> for WatchEventType {
|
||||||
fn from(e: DebouncedEvent) -> WatchEventType {
|
fn from(e: DebouncedEvent) -> WatchEventType {
|
||||||
match e {
|
match e {
|
||||||
|
29
src/file.rs
29
src/file.rs
@ -16,6 +16,7 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use glob;
|
||||||
use notify::{watcher, RecursiveMode, Watcher};
|
use notify::{watcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
use error::CommandError;
|
use error::CommandError;
|
||||||
@ -27,6 +28,7 @@ pub struct FileProcess<'a> {
|
|||||||
cmd: &'a str,
|
cmd: &'a str,
|
||||||
env: Option<Vec<String>>,
|
env: Option<Vec<String>>,
|
||||||
files: Vec<&'a str>,
|
files: Vec<&'a str>,
|
||||||
|
exclude: Option<Vec<&'a str>>,
|
||||||
method: WatchEventType,
|
method: WatchEventType,
|
||||||
poll: Option<Duration>,
|
poll: Option<Duration>,
|
||||||
}
|
}
|
||||||
@ -36,6 +38,7 @@ impl<'a> FileProcess<'a> {
|
|||||||
cmd: &'a str,
|
cmd: &'a str,
|
||||||
env: Option<Vec<String>>,
|
env: Option<Vec<String>>,
|
||||||
file: Vec<&'a str>,
|
file: Vec<&'a str>,
|
||||||
|
exclude: Option<Vec<&'a str>>,
|
||||||
method: WatchEventType,
|
method: WatchEventType,
|
||||||
poll: Option<Duration>,
|
poll: Option<Duration>,
|
||||||
) -> FileProcess<'a> {
|
) -> FileProcess<'a> {
|
||||||
@ -44,6 +47,7 @@ impl<'a> FileProcess<'a> {
|
|||||||
env,
|
env,
|
||||||
method,
|
method,
|
||||||
poll,
|
poll,
|
||||||
|
exclude,
|
||||||
files: file,
|
files: file,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,6 +126,7 @@ fn wait_for_fs_events(
|
|||||||
ch: Sender<()>,
|
ch: Sender<()>,
|
||||||
method: WatchEventType,
|
method: WatchEventType,
|
||||||
files: &Vec<&str>,
|
files: &Vec<&str>,
|
||||||
|
excluded: &Option<Vec<&str>>,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
// Notify requires a channel for communication.
|
// Notify requires a channel for communication.
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
@ -137,9 +142,29 @@ fn wait_for_fs_events(
|
|||||||
watcher.watch(*file, RecursiveMode::Recursive)?;
|
watcher.watch(*file, RecursiveMode::Recursive)?;
|
||||||
println!("Watching {:?}", *file);
|
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 {
|
loop {
|
||||||
let evt: WatchEventType = match rx.recv() {
|
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) => {
|
Err(e) => {
|
||||||
println!("Watch Error: {}", e);
|
println!("Watch Error: {}", e);
|
||||||
WatchEventType::Error
|
WatchEventType::Error
|
||||||
@ -175,7 +200,7 @@ impl<'a> Process for FileProcess<'a> {
|
|||||||
watch_for_change_events(rx, cmd, env, poll);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -16,6 +16,7 @@
|
|||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate humantime;
|
extern crate humantime;
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
extern crate glob;
|
||||||
|
|
||||||
use std::{process, str::FromStr};
|
use std::{process, str::FromStr};
|
||||||
|
|
||||||
@ -47,6 +48,10 @@ fn do_flags() -> clap::ArgMatches {
|
|||||||
arg!(-f --file).name("file")
|
arg!(-f --file).name("file")
|
||||||
.takes_value(true).help("File or directory to watch for changes"),
|
.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!(--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")))
|
.arg(arg!(--poll).name("poll").takes_value(true).value_parser(value_parser!(humantime::Duration)).help("Duration of time between polls")))
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@ -90,8 +95,12 @@ fn main() {
|
|||||||
Some(d) => Some((*d).into()),
|
Some(d) => Some((*d).into()),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
let exclude = match matches.values_of("exclude") {
|
||||||
|
Some(vr) => Some(vr.collect()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
println!("Enforcing a poll time of {:?}", duration);
|
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") {
|
} else if let Some(matches) = app.subcommand_matches("timer") {
|
||||||
// TODO(jwall): This should use cancelable commands.
|
// TODO(jwall): This should use cancelable commands.
|
||||||
// Unwrap because this flag is required.
|
// Unwrap because this flag is required.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user