use crate::oyster::Oyster;
use pathbufd::PathBufD;
use rusqlite::{Connection, Result};
use std::hash::Hash;
use std::{fs, sync::LazyLock};
#[cfg(not(feature = "sqlite_backend"))]
use crate::pearl::{Pearl, ResourceDescriptor};
pub static PATH: LazyLock<PathBufD> = LazyLock::new(|| PathBufD::current().extend(&["dump.db"]));
impl<K, V> Oyster<K, V>
where
K: Hash + Ord + Clone + Send + ToString + From<String>,
V: Clone + Send + ToString + From<String>,
{
#[cfg(not(feature = "sqlite_backend"))]
pub fn dump(&self) -> Result<()> {
if !fs::exists(PATH.as_ref()).unwrap_or(false) {
if let Err(e) = fs::write(PATH.to_string(), []) {
panic!("{}", e);
};
}
let conn = Connection::open(PATH.as_ref())?;
conn.pragma_update(None, "journal_mode", "WAL")?;
conn.execute(
"CREATE TABLE IF NOT EXISTS map (
key TEXT NOT NULL,
value TEXT NOT NULL,
used INT
)",
(),
)?;
for (k, v) in &self.0 {
self.write_into_db(k, v).unwrap();
}
Ok(())
}
#[cfg(not(feature = "sqlite_backend"))]
pub fn restore(&mut self) -> Result<()> {
if !fs::exists(PATH.as_ref()).unwrap_or(false) {
return Ok(());
}
let conn = Connection::open(PATH.as_ref())?;
let mut query = conn.prepare("SELECT \"key\", \"value\", \"used\" FROM \"map\"")?;
let iter = query.query_map([], |row| {
Ok((
row.get::<usize, String>(0)?,
row.get::<usize, String>(1)?,
row.get::<usize, usize>(2)?,
))
})?;
for i in iter {
let (key, value, used) = i.unwrap();
self.insert_full(
key.into(),
Pearl(
value.into(),
ResourceDescriptor {
#[cfg(feature = "lru")]
used,
},
),
);
}
Ok(())
}
#[cfg(not(feature = "sqlite_backend"))]
pub fn write_into_db(&self, k: &K, v: &Pearl<V>) -> Result<()> {
let conn = Connection::open(PATH.as_ref())?;
let mut query = conn.prepare("SELECT * FROM \"map\" WHERE \"key\" = ?")?;
let selected = query.query_row([k.to_string()], |row| {
Ok((
row.get::<usize, String>(0)?,
row.get::<usize, String>(1)?,
row.get::<usize, usize>(2)?,
))
});
if selected.is_err() {
conn.execute(
"INSERT INTO \"map\" VALUES (?, ?, ?)",
(k.to_string(), v.0.to_string(), v.1.used),
)?;
} else if let Ok(existing) = selected {
if (existing.1 != v.0.to_string()) | (existing.2 != v.1.used) {
conn.execute(
"UPDATE \"map\" SET \"value\" = ?, \"used\" = ? WHERE \"key\" = ?",
(v.0.to_string(), v.1.used, k.to_string()),
)?;
}
}
Ok(())
}
#[cfg(not(feature = "sqlite_backend"))]
pub fn remove_from_db(&self, key: &K) -> Result<()> {
if !fs::exists(PATH.as_ref()).unwrap_or(false) {
return Ok(());
}
let conn = Connection::open(PATH.as_ref())?;
conn.execute("DELETE FROM \"map\" WHERE \"key\" = ?", [key.to_string()])?;
Ok(())
}
#[cfg(feature = "sqlite_backend")]
pub fn dump(&self) -> Result<()> {
if !fs::exists(PATH.as_ref()).unwrap_or(false) {
if let Err(e) = fs::write(PATH.to_string(), []) {
panic!("{}", e);
};
}
let conn = Connection::open(PATH.as_ref())?;
conn.pragma_update(None, "journal_mode", "WAL")?;
conn.execute(
"CREATE TABLE IF NOT EXISTS map (
key TEXT NOT NULL,
value TEXT NOT NULL,
used INT
)",
(),
)?;
conn.execute("ATTACH DATABASE 'staging.db' AS staging_db", [])?;
conn.execute("INSERT INTO map SELECT * FROM staging_db.map", [])?;
Ok(())
}
#[cfg(feature = "sqlite_backend")]
pub fn restore(&mut self) -> Result<()> {
if !fs::exists(PATH.as_ref()).unwrap_or(false) {
if let Err(e) = fs::write(PATH.to_string(), []) {
panic!("{}", e);
};
let conn = Connection::open(PATH.as_ref())?;
conn.pragma_update(None, "journal_mode", "WAL")?;
conn.execute(
"CREATE TABLE IF NOT EXISTS map (
key TEXT NOT NULL,
value TEXT NOT NULL,
used INT
)",
(),
)?;
}
let conn = crate::sqlite_backend::connect()?;
conn.execute("ATTACH DATABASE 'dump.db' AS disk_db", [])?;
conn.execute("INSERT INTO map SELECT * FROM disk_db.map", [])?;
Ok(())
}
}