Merge pull request #82 from PR0M3TH3AN/codex/add-to_db_path-function-and-update-usages

Normalize db paths
This commit is contained in:
thePR0M3TH3AN
2025-05-24 21:59:42 -04:00
committed by GitHub
6 changed files with 41 additions and 12 deletions

View File

@@ -18,6 +18,8 @@ use rusqlite::{
use std::result::Result as StdResult; use std::result::Result as StdResult;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use crate::utils::to_db_path;
/* ─── schema version ───────────────────────────────────────────────── */ /* ─── schema version ───────────────────────────────────────────────── */
/// Current library schema version. /// Current library schema version.
@@ -394,9 +396,13 @@ pub fn take_dirty(conn: &Connection) -> Result<Vec<i64>> {
/* ─── rename helpers ────────────────────────────────────────────── */ /* ─── rename helpers ────────────────────────────────────────────── */
pub fn update_file_path(conn: &Connection, old_path: &str, new_path: &str) -> Result<()> { 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| { let old_path = to_db_path(old_path);
r.get(0) 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( conn.execute(
"UPDATE files SET path = ?1 WHERE id = ?2", "UPDATE files SET path = ?1 WHERE id = ?2",
params![new_path, file_id], 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<()> { 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 like_pattern = format!("{}/%", old_dir.trim_end_matches('/'));
let ids = { let ids = {
let mut stmt = conn.prepare("SELECT id FROM files WHERE path LIKE ?1")?; let mut stmt = conn.prepare("SELECT id FROM files WHERE path LIKE ?1")?;

View File

@@ -1,6 +1,7 @@
// libmarlin/src/db_tests.rs // libmarlin/src/db_tests.rs
use super::db; use super::db;
use crate::utils::to_db_path;
use rusqlite::Connection; use rusqlite::Connection;
use tempfile::tempdir; use tempfile::tempdir;
@@ -283,7 +284,7 @@ fn tables_exist_and_fts_triggers() {
.unwrap() .unwrap()
.collect::<std::result::Result<Vec<_>, _>>() .collect::<std::result::Result<Vec<_>, _>>()
.unwrap(); .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<String> = marlin let hits_attr: Vec<String> = marlin
.conn() .conn()
@@ -293,5 +294,5 @@ fn tables_exist_and_fts_triggers() {
.unwrap() .unwrap()
.collect::<std::result::Result<Vec<_>, _>>() .collect::<std::result::Result<Vec<_>, _>>()
.unwrap(); .unwrap();
assert!(hits_attr.contains(&file_path.to_string_lossy().into_owned())); assert!(hits_attr.contains(&to_db_path(&file_path)));
} }

View File

@@ -3,6 +3,8 @@
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use crate::utils::to_db_path;
use anyhow::Result; use anyhow::Result;
use rusqlite::{params, Connection}; use rusqlite::{params, Connection};
use tracing::{debug, info}; use tracing::{debug, info};
@@ -51,7 +53,7 @@ pub fn scan_directory(conn: &mut Connection, root: &Path) -> Result<usize> {
.as_secs() as i64; .as_secs() as i64;
// Execute the upsert // Execute the upsert
let path_str = path.to_string_lossy(); let path_str = to_db_path(path);
stmt.execute(params![path_str, size, mtime])?; stmt.execute(params![path_str, size, mtime])?;
count += 1; count += 1;

View File

@@ -1,6 +1,6 @@
//! Misc shared helpers. //! Misc shared helpers.
use std::path::PathBuf; use std::path::{Path, PathBuf};
/// Determine a filesystem root to limit recursive walking on glob scans. /// Determine a filesystem root to limit recursive walking on glob scans.
/// ///
@@ -44,3 +44,19 @@ pub fn determine_scan_root(pattern: &str) -> PathBuf {
root 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: AsRef<Path>>(p: P) -> String {
let s = p.as_ref().to_string_lossy();
#[cfg(windows)]
{
s.replace('\\', "/")
}
#[cfg(not(windows))]
{
s.into_owned()
}
}

View File

@@ -6,6 +6,7 @@
//! watcher can be paused, resumed and shut down cleanly. //! watcher can be paused, resumed and shut down cleanly.
use crate::db::{self, Database}; use crate::db::{self, Database};
use crate::utils::to_db_path;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use crossbeam_channel::{bounded, Receiver}; use crossbeam_channel::{bounded, Receiver};
use notify::{ use notify::{
@@ -479,8 +480,8 @@ impl FileWatcher {
// update DB for renames // update DB for renames
if let EventKind::Modify(ModifyKind::Name(_)) = ev.kind { if let EventKind::Modify(ModifyKind::Name(_)) = ev.kind {
if let (Some(old_p), Some(new_p)) = (&ev.old_path, &ev.new_path) { if let (Some(old_p), Some(new_p)) = (&ev.old_path, &ev.new_path) {
let old_s = old_p.to_string_lossy(); let old_s = to_db_path(old_p);
let new_s = new_p.to_string_lossy(); let new_s = to_db_path(new_p);
let res = let res =
handle_db_update(db_mutex, &old_s, &new_s, new_p.is_dir()); handle_db_update(db_mutex, &old_s, &new_s, new_p.is_dir());
if let Err(e) = res { if let Err(e) = res {

View File

@@ -6,6 +6,7 @@ mod tests {
use crate::backup::BackupManager; use crate::backup::BackupManager;
// These are still from the watcher module // These are still from the watcher module
use crate::db::open as open_marlin_db; 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::watcher::{FileWatcher, WatcherConfig, WatcherState}; // Use your project's DB open function
use crate::Marlin; use crate::Marlin;
@@ -29,7 +30,7 @@ mod tests {
.conn() .conn()
.query_row( .query_row(
"SELECT COUNT(*) FROM files WHERE path = ?1", "SELECT COUNT(*) FROM files WHERE path = ?1",
[path.to_string_lossy()], [to_db_path(path)],
|r| r.get(0), |r| r.get(0),
) )
.unwrap(); .unwrap();
@@ -211,7 +212,7 @@ mod tests {
.conn() .conn()
.query_row( .query_row(
"SELECT COUNT(*) FROM files WHERE path = ?1", "SELECT COUNT(*) FROM files WHERE path = ?1",
[new_file.to_string_lossy()], [to_db_path(&new_file)],
|r| r.get(0), |r| r.get(0),
) )
.unwrap(); .unwrap();
@@ -262,7 +263,7 @@ mod tests {
.conn() .conn()
.query_row( .query_row(
"SELECT COUNT(*) FROM files WHERE path = ?1", "SELECT COUNT(*) FROM files WHERE path = ?1",
[p.to_string_lossy()], [to_db_path(&p)],
|r| r.get(0), |r| r.get(0),
) )
.unwrap(); .unwrap();