use std::path::Path;
use rusqlite::{Connection, params};
use super::Storage;
use crate::bridge_protocol::{DirectoryBookmarkRecord, DirectoryHistoryRecord, now_millis};
use crate::directory::{
canonicalize_directory, default_display_name, normalize_absolute_directory,
};
impl Storage {
pub fn list_directory_bookmarks(&self) -> anyhow::Result<Vec<DirectoryBookmarkRecord>> {
let conn = self.connect()?;
let mut stmt = conn.prepare(
"SELECT path, display_name, created_at_ms, updated_at_ms
FROM directory_bookmarks
ORDER BY display_name COLLATE NOCASE ASC",
)?;
let rows = stmt.query_map([], |row| {
Ok(DirectoryBookmarkRecord {
path: row.get(0)?,
display_name: row.get(1)?,
created_at_ms: row.get(2)?,
updated_at_ms: row.get(3)?,
})
})?;
Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
}
pub fn upsert_directory_bookmark(
&self,
path: &Path,
display_name: Option<&str>,
) -> anyhow::Result<DirectoryBookmarkRecord> {
let canonical = canonicalize_directory(path)?;
let path_string = canonical.to_string_lossy().to_string();
let now = now_millis();
let conn = self.connect()?;
let display_name = display_name
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
.unwrap_or_else(|| default_display_name(&canonical));
let inserted = insert_directory_bookmark_row(&conn, &path_string, &display_name, now, now)?;
if inserted == 0 {
update_directory_bookmark_row(&conn, &path_string, &display_name, now)?;
}
let created_at_ms = directory_bookmark_created_at(&conn, &path_string)?;
Ok(DirectoryBookmarkRecord {
path: canonical.to_string_lossy().to_string(),
display_name,
created_at_ms,
updated_at_ms: now,
})
}
pub fn remove_directory_bookmark(&self, path: &Path) -> anyhow::Result<()> {
let canonical = normalize_absolute_directory(path)?;
let conn = self.connect()?;
conn.execute(
"DELETE FROM directory_bookmarks WHERE path = ?1",
params![canonical.to_string_lossy().to_string()],
)?;
Ok(())
}
pub fn list_directory_history(
&self,
limit: usize,
) -> anyhow::Result<Vec<DirectoryHistoryRecord>> {
let conn = self.connect()?;
let mut stmt = conn.prepare(
"SELECT path, last_used_at_ms, use_count
FROM directory_history
ORDER BY last_used_at_ms DESC
LIMIT ?1",
)?;
let rows = stmt.query_map(params![limit.max(1) as i64], |row| {
let path: String = row.get(0)?;
Ok(DirectoryHistoryRecord {
display_name: default_display_name(Path::new(&path)),
path,
last_used_at_ms: row.get(1)?,
use_count: row.get(2)?,
})
})?;
Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
}
pub fn record_directory_usage(&self, path: &Path) -> anyhow::Result<DirectoryHistoryRecord> {
let normalized = normalize_absolute_directory(path)?;
let path_string = normalized.to_string_lossy().to_string();
let now = now_millis();
let conn = self.connect()?;
let inserted = insert_directory_history_row(&conn, &path_string, now)?;
if inserted == 0 {
increment_directory_history_row(&conn, &path_string, now)?;
}
let use_count = directory_history_use_count(&conn, &path_string)?;
Ok(DirectoryHistoryRecord {
path: normalized.to_string_lossy().to_string(),
display_name: default_display_name(&normalized),
last_used_at_ms: now,
use_count,
})
}
}
fn update_directory_bookmark_row(
conn: &Connection,
path: &str,
display_name: &str,
updated_at_ms: i64,
) -> rusqlite::Result<()> {
conn.execute(
"UPDATE directory_bookmarks
SET display_name = ?2, updated_at_ms = ?3
WHERE path = ?1",
params![path, display_name, updated_at_ms],
)?;
Ok(())
}
fn directory_bookmark_created_at(conn: &Connection, path: &str) -> rusqlite::Result<i64> {
conn.query_row(
"SELECT created_at_ms FROM directory_bookmarks WHERE path = ?1",
params![path],
|row| row.get(0),
)
}
fn insert_directory_bookmark_row(
conn: &Connection,
path: &str,
display_name: &str,
created_at_ms: i64,
updated_at_ms: i64,
) -> rusqlite::Result<usize> {
conn.execute(
"INSERT OR IGNORE INTO directory_bookmarks (path, display_name, created_at_ms, updated_at_ms)
VALUES (?1, ?2, ?3, ?4)",
params![path, display_name, created_at_ms, updated_at_ms],
)
}
fn directory_history_use_count(conn: &Connection, path: &str) -> rusqlite::Result<i64> {
conn.query_row(
"SELECT use_count FROM directory_history WHERE path = ?1",
params![path],
|row| row.get(0),
)
}
fn increment_directory_history_row(
conn: &Connection,
path: &str,
last_used_at_ms: i64,
) -> rusqlite::Result<()> {
conn.execute(
"UPDATE directory_history
SET last_used_at_ms = ?2, use_count = use_count + 1
WHERE path = ?1",
params![path, last_used_at_ms],
)?;
Ok(())
}
fn insert_directory_history_row(
conn: &Connection,
path: &str,
last_used_at_ms: i64,
) -> rusqlite::Result<usize> {
conn.execute(
"INSERT OR IGNORE INTO directory_history (path, last_used_at_ms, use_count)
VALUES (?1, ?2, 1)",
params![path, last_used_at_ms],
)
}