From c24f84a81d92804ceb3b62e4ae33cef3e6df7344 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 May 2025 14:33:21 -0400 Subject: [PATCH 1/2] Normalize paths for watcher --- libmarlin/src/utils.rs | 8 +++++++- libmarlin/src/watcher.rs | 15 +++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/libmarlin/src/utils.rs b/libmarlin/src/utils.rs index feb16ba..ecadf9c 100644 --- a/libmarlin/src/utils.rs +++ b/libmarlin/src/utils.rs @@ -45,12 +45,18 @@ pub fn determine_scan_root(pattern: &str) -> PathBuf { } } +/// Canonicalize a path, falling back to the original on error. +pub fn canonicalize_lossy>(p: P) -> PathBuf { + std::fs::canonicalize(&p).unwrap_or_else(|_| p.as_ref().to_path_buf()) +} + /// Convert a filesystem path to a normalized database path. /// /// On Windows this replaces backslashes with forward slashes so that paths /// stored in the database are consistent across platforms. pub fn to_db_path>(p: P) -> String { - let s = p.as_ref().to_string_lossy(); + let canonical = canonicalize_lossy(p); + let s = canonical.to_string_lossy(); #[cfg(windows)] { s.replace('\\', "/") diff --git a/libmarlin/src/watcher.rs b/libmarlin/src/watcher.rs index d33b3ed..ec3b083 100644 --- a/libmarlin/src/watcher.rs +++ b/libmarlin/src/watcher.rs @@ -6,7 +6,7 @@ //! watcher can be paused, resumed and shut down cleanly. use crate::db::{self, Database}; -use crate::utils::to_db_path; +use crate::utils::{canonicalize_lossy, to_db_path}; use anyhow::{anyhow, Context, Result}; use crossbeam_channel::{bounded, Receiver}; use notify::{ @@ -256,13 +256,20 @@ impl FileWatcher { // ── start actual OS watcher ─────────────────────────────────────────── let event_tx = tx.clone(); let mut actual_watcher = RecommendedWatcher::new( - move |ev| { + move |ev: Result| { + let ev = ev.map(|mut e| { + for p in &mut e.paths { + *p = canonicalize_lossy(&*p); + } + e + }); let _ = event_tx.send(ev); }, notify::Config::default(), )?; - for p in &paths { + let canonical_paths: Vec = paths.iter().map(canonicalize_lossy).collect(); + for p in &canonical_paths { actual_watcher .watch(p, RecursiveMode::Recursive) .with_context(|| format!("Failed to watch path {}", p.display()))?; @@ -532,7 +539,7 @@ impl FileWatcher { Ok(Self { state, _config: config, - watched_paths: paths, + watched_paths: canonical_paths, _event_receiver: rx, _watcher: actual_watcher, processor_thread: Some(processor_thread), From 892a444b3248b3c433ae0e2390052d75c4dc2c8e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 May 2025 16:51:39 -0400 Subject: [PATCH 2/2] Canonicalize test paths --- libmarlin/src/utils.rs | 8 +++++++- libmarlin/src/watcher.rs | 15 +++++++++++---- libmarlin/src/watcher_tests.rs | 7 +++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/libmarlin/src/utils.rs b/libmarlin/src/utils.rs index feb16ba..ecadf9c 100644 --- a/libmarlin/src/utils.rs +++ b/libmarlin/src/utils.rs @@ -45,12 +45,18 @@ pub fn determine_scan_root(pattern: &str) -> PathBuf { } } +/// Canonicalize a path, falling back to the original on error. +pub fn canonicalize_lossy>(p: P) -> PathBuf { + std::fs::canonicalize(&p).unwrap_or_else(|_| p.as_ref().to_path_buf()) +} + /// Convert a filesystem path to a normalized database path. /// /// On Windows this replaces backslashes with forward slashes so that paths /// stored in the database are consistent across platforms. pub fn to_db_path>(p: P) -> String { - let s = p.as_ref().to_string_lossy(); + let canonical = canonicalize_lossy(p); + let s = canonical.to_string_lossy(); #[cfg(windows)] { s.replace('\\', "/") diff --git a/libmarlin/src/watcher.rs b/libmarlin/src/watcher.rs index d33b3ed..ec3b083 100644 --- a/libmarlin/src/watcher.rs +++ b/libmarlin/src/watcher.rs @@ -6,7 +6,7 @@ //! watcher can be paused, resumed and shut down cleanly. use crate::db::{self, Database}; -use crate::utils::to_db_path; +use crate::utils::{canonicalize_lossy, to_db_path}; use anyhow::{anyhow, Context, Result}; use crossbeam_channel::{bounded, Receiver}; use notify::{ @@ -256,13 +256,20 @@ impl FileWatcher { // ── start actual OS watcher ─────────────────────────────────────────── let event_tx = tx.clone(); let mut actual_watcher = RecommendedWatcher::new( - move |ev| { + move |ev: Result| { + let ev = ev.map(|mut e| { + for p in &mut e.paths { + *p = canonicalize_lossy(&*p); + } + e + }); let _ = event_tx.send(ev); }, notify::Config::default(), )?; - for p in &paths { + let canonical_paths: Vec = paths.iter().map(canonicalize_lossy).collect(); + for p in &canonical_paths { actual_watcher .watch(p, RecursiveMode::Recursive) .with_context(|| format!("Failed to watch path {}", p.display()))?; @@ -532,7 +539,7 @@ impl FileWatcher { Ok(Self { state, _config: config, - watched_paths: paths, + watched_paths: canonical_paths, _event_receiver: rx, _watcher: actual_watcher, processor_thread: Some(processor_thread), diff --git a/libmarlin/src/watcher_tests.rs b/libmarlin/src/watcher_tests.rs index 9bafbb6..ae4f37a 100644 --- a/libmarlin/src/watcher_tests.rs +++ b/libmarlin/src/watcher_tests.rs @@ -6,7 +6,7 @@ mod tests { use crate::backup::BackupManager; // These are still from the watcher module use crate::db::open as open_marlin_db; - use crate::utils::to_db_path; + use crate::utils::{canonicalize_lossy, to_db_path}; use crate::watcher::{FileWatcher, WatcherConfig, WatcherState}; // Use your project's DB open function use crate::Marlin; @@ -24,13 +24,14 @@ mod tests { expected: i64, timeout: Duration, ) { + let target = canonicalize_lossy(path); let start = Instant::now(); loop { let count: i64 = marlin .conn() .query_row( "SELECT COUNT(*) FROM files WHERE path = ?1", - [to_db_path(path)], + [to_db_path(&target)], |r| r.get(0), ) .unwrap(); @@ -201,6 +202,7 @@ mod tests { thread::sleep(Duration::from_millis(100)); let new_file = dir.join("b.txt"); fs::rename(&file, &new_file).unwrap(); + let new_file = canonicalize_lossy(&new_file); wait_for_row_count(&marlin, &new_file, 1, Duration::from_secs(10)); watcher.stop().unwrap(); assert!( @@ -247,6 +249,7 @@ mod tests { thread::sleep(Duration::from_millis(100)); let new = dir.join("newdir"); fs::rename(&sub, &new).unwrap(); + let new = canonicalize_lossy(&new); for fname in ["one.txt", "two.txt"] { let p = new.join(fname); wait_for_row_count(&marlin, &p, 1, Duration::from_secs(10));