diff --git a/Cargo.lock b/Cargo.lock index 4d24034..904ff5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "filetime" version = "0.2.22" @@ -350,7 +356,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi", "io-lifetimes", - "rustix", + "rustix 0.37.11", "windows-sys 0.48.0", ] @@ -391,9 +397,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" @@ -401,6 +407,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + [[package]] name = "lock_api" version = "0.4.9" @@ -581,7 +593,20 @@ dependencies = [ "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.7", "windows-sys 0.48.0", ] @@ -681,6 +706,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix 0.38.14", + "windows-sys 0.48.0", +] + [[package]] name = "turm" version = "0.6.0" @@ -693,6 +731,7 @@ dependencies = [ "notify", "ratatui", "regex", + "tempfile", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 424eb6f..bb11524 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ lazy_static = "1.4.0" notify = "6.1.1" regex = "1.7.1" ratatui = "0.23.0" +tempfile = "3.8.0" diff --git a/src/file_watcher.rs b/src/file_watcher.rs index 54d066a..c562a89 100644 --- a/src/file_watcher.rs +++ b/src/file_watcher.rs @@ -12,6 +12,7 @@ use crossbeam::{ select, }; use notify::{event::ModifyKind, RecursiveMode, Watcher}; +use tempfile::NamedTempFile; use crate::app::AppMessage; @@ -70,7 +71,10 @@ impl FileWatcher { fn run(&mut self) -> Result<(), RecvError> { let (watch_sender, watch_receiver) = unbounded(); let mut watcher = notify::recommended_watcher(move |res: notify::Result| { - let event = res.unwrap(); + let event = match res { + Ok(e) => e, + Err(_) => return, + }; match event.kind { notify::EventKind::Modify(ModifyKind::Data(_)) => { watch_sender.send(event.paths).unwrap(); @@ -80,6 +84,49 @@ impl FileWatcher { }) .unwrap(); + // Check if the file watcher limit is reached by creating a file, watching it and then deleting it + let tmp_file = NamedTempFile::new().unwrap(); + let tmp_file_path = tmp_file.path(); + let watcher_result = watcher.watch(tmp_file_path, RecursiveMode::NonRecursive); + let max_watches_reached = match watcher_result { + Err(notify::Error { + kind: notify::ErrorKind::MaxFilesWatch, + .. + }) => true, + Ok(_) => { + watcher.unwatch(tmp_file_path).unwrap(); + false + } + _ => false, + }; + let _ = tmp_file.close(); + + let config = notify::Config::default() + .with_poll_interval(Duration::from_secs(2)) + .with_compare_contents(false); + + let mut watcher = if max_watches_reached { + let (watch_sender, _) = unbounded(); + let watcher = notify::PollWatcher::new( + move |res: notify::Result| { + let event = match res { + Ok(e) => e, + Err(_) => return, + }; + match event.kind { + notify::EventKind::Modify(ModifyKind::Data(_)) => { + let _ = watch_sender.send(event.paths); + } + _ => {} + }; + }, + config, + ); + Box::new(watcher.unwrap()) as Box + } else { + Box::new(watcher) as Box + }; + let (mut _content_sender, mut _content_receiver) = unbounded::>(); let (mut _watch_sender, mut _watch_receiver) = unbounded::<()>(); loop { @@ -91,7 +138,7 @@ impl FileWatcher { (_watch_sender, _watch_receiver) = unbounded::<()>(); if let Some(p) = &self.file_path { - watcher.unwatch(p).expect(format!("Failed to unwatch {:?}", p).as_str()); + let _ = watcher.unwatch(p); self.file_path = None; } @@ -177,9 +224,7 @@ impl FileWatcherHandle { pub fn set_file_path(&mut self, file_path: Option) { if self.file_path != file_path { self.file_path = file_path.clone(); - self.sender - .send(FileWatcherMessage::FilePath(file_path)) - .unwrap(); + let _ = self.sender.send(FileWatcherMessage::FilePath(file_path)); } } }