use anyhow::Result;
use rusqlite::OptionalExtension;
use super::MetadataStore;
use super::SQLITE_MAX_BIND_VARS;
impl MetadataStore {
pub fn get_file_hash(&self, repo: &str, file_path: &str) -> Result<Option<String>> {
let mut stmt = self
.conn
.prepare("SELECT hash FROM file_hashes WHERE repo = ?1 AND file_path = ?2")?;
let result = stmt
.query_row([repo, file_path], |row| row.get(0))
.optional()?;
Ok(result)
}
pub fn set_file_hash(&self, repo: &str, file_path: &str, hash: &str) -> Result<()> {
self.conn.execute(
"INSERT OR REPLACE INTO file_hashes (repo, file_path, hash) VALUES (?1, ?2, ?3)",
[repo, file_path, hash],
)?;
Ok(())
}
pub fn set_file_hashes_bulk(&self, repo: &str, entries: &[(&str, &str)]) -> Result<()> {
let tx = self.conn.unchecked_transaction()?;
{
let mut stmt = tx.prepare(
"INSERT OR REPLACE INTO file_hashes (repo, file_path, hash) VALUES (?1, ?2, ?3)",
)?;
for (path, hash) in entries {
stmt.execute([repo, path, hash])?;
}
}
tx.commit()?;
Ok(())
}
pub fn delete_file_hashes(&self, repo: Option<&str>, file_paths: &[String]) -> Result<()> {
if file_paths.is_empty() {
return Ok(());
}
let tx = self.conn.unchecked_transaction()?;
for chunk in file_paths.chunks(SQLITE_MAX_BIND_VARS) {
let placeholders: Vec<String> = (1..=chunk.len()).map(|i| format!("?{i}")).collect();
let sql = match repo {
Some(_) => format!(
"DELETE FROM file_hashes WHERE repo = ?{} AND file_path IN ({})",
chunk.len() + 1,
placeholders.join(", ")
),
None => format!(
"DELETE FROM file_hashes WHERE file_path IN ({})",
placeholders.join(", ")
),
};
let mut params: Vec<&dyn rusqlite::ToSql> =
chunk.iter().map(|p| p as &dyn rusqlite::ToSql).collect();
if let Some(ref r) = repo {
params.push(r as &dyn rusqlite::ToSql);
}
tx.execute(&sql, params.as_slice())?;
}
tx.commit()?;
Ok(())
}
pub fn get_all_indexed_files(&self, repo: &str) -> Result<std::collections::HashSet<String>> {
let mut stmt = self
.conn
.prepare("SELECT file_path FROM file_hashes WHERE repo = ?1")?;
let rows = stmt.query_map([repo], |row| row.get::<_, String>(0))?;
let mut result = std::collections::HashSet::new();
for row in rows {
result.insert(row?);
}
Ok(result)
}
pub fn clear_file_hashes(&self, repo: &str) -> Result<()> {
self.conn
.execute("DELETE FROM file_hashes WHERE repo = ?1", [repo])?;
Ok(())
}
}