From beed5787ac64553a719a3958da1473e31be84ba9 Mon Sep 17 00:00:00 2001 From: Jason Longshore Date: Tue, 26 Mar 2019 23:21:34 -0500 Subject: [PATCH] Fixes for the next release given recent usage * Rework design to not watch ignored directories, improving reliability and resource utilization * Reduce debouncing period to 125ms * Mark fsw output with "fsw:" * Bump notify and transitive dependencies There's probably some bugs here, but if so I'll do another release. --- Cargo.lock | 38 +++++++++--------- Cargo.toml | 2 +- README.md | 9 ++++- src/main.rs | 109 +++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 106 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb83242..892b59c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -18,8 +18,8 @@ name = "filetime" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -30,7 +30,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -38,14 +38,14 @@ name = "fsevent-sys" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fsw" version = "0.0.1" dependencies = [ - "notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -69,7 +69,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -77,7 +77,7 @@ name = "inotify-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -85,7 +85,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -105,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.49" +version = "0.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -113,7 +113,7 @@ name = "log" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -126,7 +126,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -161,14 +161,14 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "notify" -version = "4.0.9" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -177,7 +177,7 @@ dependencies = [ "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -261,7 +261,7 @@ dependencies = [ [metadata] "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" "checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05" "checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874" @@ -272,13 +272,13 @@ dependencies = [ "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)" = "413f3dfc802c5dc91dc570b05125b6cda9855edfaa9825c9849807876376e70e" +"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9cc7ed2bd4b7edad3ee93b659c38e53dabb619f7274e127a0fab054ad2bb998d" +"checksum notify 4.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "abb1581693e44d8a0ec347ef12289625063f52a1dddc3f3c9befd5fc59e88943" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" diff --git a/Cargo.toml b/Cargo.toml index 2ad6caa..247d3ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ description = "A tool to watch a directory and run a command when its contents c readme = "README.md" [dependencies] -notify = "4.0.0" +notify = "4.0.10" diff --git a/README.md b/README.md index cdf949a..9fcd14c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ fsw is a tool for recursively watching the current working directory and running It's integrated with Git, so it won't rerun the command if an ignored file changes. -Why? Well, I quite like the workflow that sbt's tidle (`~`) operator provides, and I wanted a reliable mechanism to do the same thing with other tools. +Why? Well, I quite like the workflow that sbt's tilde (`~`) operator provides, and I wanted a reliable mechanism to do the same thing with other tools. ## Install @@ -28,6 +28,13 @@ fsw []... ## Changelog +### 0.1.1 - 2019-03-26 + +* Rework design to not watch ignored directories, improving reliability and resource utilization +* Reduce debouncing period to 125ms +* Mark fsw output with "fsw:" +* Bump notify and transitive dependencies + ### 0.1.0 - 2019-02-26 * Initial release. diff --git a/src/main.rs b/src/main.rs index dd6c7d6..2f689b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,23 @@ extern crate notify; -const DRAIN_MS: u64 = 250; +const DRAIN_MS: u64 = 125; +const GIT_PATH: &str = ".git"; -use notify::{raw_watcher, RecursiveMode, Watcher}; +use notify::{raw_watcher, RecommendedWatcher, RecursiveMode, Watcher}; use std::{ - env, io, path, process, + collections::HashSet, + env, fs, io, path, process, sync::mpsc::{channel, Receiver, Sender}, thread, time, }; enum Msg { - PathEvent(path::PathBuf), + PathEvent, ThreadFinished, } +/// Handles events (file notifcations / process notifcations) and forks a new process +/// when required. fn handle(sender: Sender, receiver: Receiver, command: String, args: Vec) { let mut running = false; @@ -22,7 +26,7 @@ fn handle(sender: Sender, receiver: Receiver, command: String, args: V while let Ok(path) = receiver.recv() { let run = match path { - Msg::PathEvent(_) => { + Msg::PathEvent => { if running { waiting = true; false @@ -57,15 +61,28 @@ fn handle(sender: Sender, receiver: Receiver, command: String, args: V let sender = sender.clone(); let command = command.clone(); let args = args.clone(); - let handle = process::Command::new(&command) - .args(args) - .stdin(process::Stdio::null()) - .stdout(process::Stdio::inherit()) - .stderr(process::Stdio::inherit()) - .spawn(); thread::spawn(move || { - handle_child(command, handle); + let status = process::Command::new(&command) + .args(args) + .stdin(process::Stdio::null()) + .stdout(process::Stdio::inherit()) + .stderr(process::Stdio::inherit()) + .status(); + + match status.map(|s| s.code()) { + Ok(Some(c)) => { + println!("fsw: {} exited with {}", command, c); + } + + Ok(None) => { + println!("fsw: {} exited with unknown", command); + } + + Err(e) => { + println!("fsw: {} failed with {}", command, e); + } + } let _ = sender.send(Msg::ThreadFinished); }); @@ -74,40 +91,62 @@ fn handle(sender: Sender, receiver: Receiver, command: String, args: V } } -fn handle_child(command: String, result: io::Result) { - match result.and_then(|mut s| s.wait()).map(|s| s.code()) { - Ok(Some(c)) => { - println!("{} exited with {}", command, c); - } +/// Recursively walk a directory, watching everything including the specified directory +/// if it isn't already watched. +/// +/// @FIXME if directories are removed, they aren't removed from the set (memory leak) +fn watch_dir( + git_dir: &path::Path, + watcher: &mut RecommendedWatcher, + watching: &mut HashSet, + dir: &path::PathBuf, +) -> io::Result<()> { + watcher + .watch(&dir, RecursiveMode::NonRecursive) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - Ok(None) => { - println!("{} exited with unknown", command); - } + for entry in fs::read_dir(dir)? { + let entry_path = entry?.path(); - Err(e) => { - println!("{} failed with {}", command, e); + if entry_path.is_dir() + && !git_ignored(git_dir, &entry_path) + && !watching.contains(&entry_path) + { + watch_dir(&git_dir, watcher, watching, &entry_path)?; + + watching.insert(entry_path); } } + + Ok(()) } +/// Watches the working directory of the process, and sends a +/// PathEvent to the provided sender if a relevant file or +/// directory changes. fn watch(sender: Sender) -> io::Result<()> { let (tx, rx) = channel(); let working_dir = env::current_dir()?; - let git_dir = working_dir.join(".git"); + let git_dir = working_dir.join(GIT_PATH); let mut watcher = raw_watcher(tx).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let mut watching = HashSet::new(); - watcher - .watch(working_dir, RecursiveMode::Recursive) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + watch_dir(&git_dir, &mut watcher, &mut watching, &working_dir)?; + + watching.insert(working_dir); while let Ok(event) = rx.recv() { if let Some(path) = event.path { - if !path.starts_with(&git_dir) && !git_ignored(&path) { + if !path.starts_with(&git_dir) && !git_ignored(&git_dir, &path) { sender - .send(Msg::PathEvent(path)) + .send(Msg::PathEvent) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + if !watching.contains(&path) && path.is_dir() { + watch_dir(&git_dir, &mut watcher, &mut watching, &path)?; + } } } } @@ -115,7 +154,15 @@ fn watch(sender: Sender) -> io::Result<()> { Ok(()) } -fn git_ignored(path: &path::Path) -> bool { +/// Determines if the provided path is ignored by git, +/// returning true if it is. +/// +/// FIXME: link to git or smth instead of forking a process +fn git_ignored(git_dir: &path::Path, path: &path::Path) -> bool { + if path.starts_with(&git_dir) { + return false; + } + if let Some(s) = path.to_str() { process::Command::new("git") .args(&["check-ignore", s]) @@ -135,9 +182,9 @@ fn main() { if args.len() < 2 { let args = args.collect::>(); - println!("fsw version: {}", env!("CARGO_PKG_VERSION")); + println!("fsw: version: {}", env!("CARGO_PKG_VERSION")); println!( - "usage: {} []...", + "fsw: usage: {} []...", args.get(0).map(|s| s.as_str()).unwrap_or("fsw") ); process::exit(1);