diff --git a/libmarlin/src/db/mod.rs b/libmarlin/src/db/mod.rs index 7edaf01..8e0b3dc 100644 --- a/libmarlin/src/db/mod.rs +++ b/libmarlin/src/db/mod.rs @@ -18,6 +18,8 @@ use rusqlite::{ use std::result::Result as StdResult; use tracing::{debug, info, warn}; +use crate::utils::to_db_path; + /* ─── schema version ───────────────────────────────────────────────── */ /// Current library schema version. @@ -394,9 +396,13 @@ pub fn take_dirty(conn: &Connection) -> Result> { /* ─── rename helpers ────────────────────────────────────────────── */ pub fn update_file_path(conn: &Connection, old_path: &str, new_path: &str) -> Result<()> { - let file_id: i64 = conn.query_row("SELECT id FROM files WHERE path = ?1", [old_path], |r| { - r.get(0) - })?; + let old_path = to_db_path(old_path); + let new_path = to_db_path(new_path); + + let file_id: i64 = + conn.query_row("SELECT id FROM files WHERE path = ?1", [&old_path], |r| { + r.get(0) + })?; conn.execute( "UPDATE files SET path = ?1 WHERE id = ?2", params![new_path, file_id], @@ -406,6 +412,8 @@ pub fn update_file_path(conn: &Connection, old_path: &str, new_path: &str) -> Re } pub fn rename_directory(conn: &mut Connection, old_dir: &str, new_dir: &str) -> Result<()> { + let old_dir = to_db_path(old_dir); + let new_dir = to_db_path(new_dir); let like_pattern = format!("{}/%", old_dir.trim_end_matches('/')); let ids = { let mut stmt = conn.prepare("SELECT id FROM files WHERE path LIKE ?1")?; diff --git a/libmarlin/src/db_tests.rs b/libmarlin/src/db_tests.rs index 76527e1..44aea74 100644 --- a/libmarlin/src/db_tests.rs +++ b/libmarlin/src/db_tests.rs @@ -1,6 +1,7 @@ // libmarlin/src/db_tests.rs use super::db; +use crate::utils::to_db_path; use rusqlite::Connection; use tempfile::tempdir; @@ -283,7 +284,7 @@ fn tables_exist_and_fts_triggers() { .unwrap() .collect::, _>>() .unwrap(); - assert!(hits_tag.contains(&file_path.to_string_lossy().into_owned())); + assert!(hits_tag.contains(&to_db_path(&file_path))); let hits_attr: Vec = marlin .conn() @@ -293,5 +294,5 @@ fn tables_exist_and_fts_triggers() { .unwrap() .collect::, _>>() .unwrap(); - assert!(hits_attr.contains(&file_path.to_string_lossy().into_owned())); + assert!(hits_attr.contains(&to_db_path(&file_path))); } diff --git a/libmarlin/src/scan.rs b/libmarlin/src/scan.rs index bf125b5..3511ae9 100644 --- a/libmarlin/src/scan.rs +++ b/libmarlin/src/scan.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; +use crate::utils::to_db_path; + use anyhow::Result; use rusqlite::{params, Connection}; use tracing::{debug, info}; @@ -51,7 +53,7 @@ pub fn scan_directory(conn: &mut Connection, root: &Path) -> Result { .as_secs() as i64; // Execute the upsert - let path_str = path.to_string_lossy(); + let path_str = to_db_path(path); stmt.execute(params![path_str, size, mtime])?; count += 1; diff --git a/libmarlin/src/utils.rs b/libmarlin/src/utils.rs index 6b6faec..feb16ba 100644 --- a/libmarlin/src/utils.rs +++ b/libmarlin/src/utils.rs @@ -1,6 +1,6 @@ //! Misc shared helpers. -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// Determine a filesystem root to limit recursive walking on glob scans. /// @@ -44,3 +44,19 @@ pub fn determine_scan_root(pattern: &str) -> PathBuf { root } } + +/// 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(); + #[cfg(windows)] + { + s.replace('\\', "/") + } + #[cfg(not(windows))] + { + s.into_owned() + } +} diff --git a/libmarlin/src/watcher.rs b/libmarlin/src/watcher.rs index 5b24033..bcd1486 100644 --- a/libmarlin/src/watcher.rs +++ b/libmarlin/src/watcher.rs @@ -6,6 +6,7 @@ //! watcher can be paused, resumed and shut down cleanly. use crate::db::{self, Database}; +use crate::utils::to_db_path; use anyhow::{anyhow, Context, Result}; use crossbeam_channel::{bounded, Receiver}; use notify::{ @@ -479,8 +480,8 @@ impl FileWatcher { // update DB for renames if let EventKind::Modify(ModifyKind::Name(_)) = ev.kind { if let (Some(old_p), Some(new_p)) = (&ev.old_path, &ev.new_path) { - let old_s = old_p.to_string_lossy(); - let new_s = new_p.to_string_lossy(); + let old_s = to_db_path(old_p); + let new_s = to_db_path(new_p); let res = handle_db_update(db_mutex, &old_s, &new_s, new_p.is_dir()); if let Err(e) = res { diff --git a/libmarlin/src/watcher_tests.rs b/libmarlin/src/watcher_tests.rs index c15c7eb..9bafbb6 100644 --- a/libmarlin/src/watcher_tests.rs +++ b/libmarlin/src/watcher_tests.rs @@ -6,6 +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::watcher::{FileWatcher, WatcherConfig, WatcherState}; // Use your project's DB open function use crate::Marlin; @@ -29,7 +30,7 @@ mod tests { .conn() .query_row( "SELECT COUNT(*) FROM files WHERE path = ?1", - [path.to_string_lossy()], + [to_db_path(path)], |r| r.get(0), ) .unwrap(); @@ -211,7 +212,7 @@ mod tests { .conn() .query_row( "SELECT COUNT(*) FROM files WHERE path = ?1", - [new_file.to_string_lossy()], + [to_db_path(&new_file)], |r| r.get(0), ) .unwrap(); @@ -262,7 +263,7 @@ mod tests { .conn() .query_row( "SELECT COUNT(*) FROM files WHERE path = ?1", - [p.to_string_lossy()], + [to_db_path(&p)], |r| r.get(0), ) .unwrap();