use crate::config::FileConfig;
use crate::error::NetabaseError;
use crate::traits::backend_store::{BackendStore, PathBasedBackend};
use crate::traits::convert::ToIVec;
use crate::traits::definition::NetabaseDefinitionTrait;
use redb::Database;
use std::marker::PhantomData;
use std::path::Path;
use std::sync::Arc;
use strum::{IntoDiscriminant, IntoEnumIterator};
use super::tree::RedbStoreTree;
pub struct RedbStore<D>
where
D: NetabaseDefinitionTrait,
<D as IntoDiscriminant>::Discriminant: crate::traits::definition::NetabaseDiscriminant,
{
pub(crate) db: Arc<Database>,
#[cfg(feature = "redb")]
pub(crate) tables: D::Tables,
pub trees: Vec<D::Discriminant>,
_phantom: PhantomData<D>,
}
impl<D> RedbStore<D>
where
D: NetabaseDefinitionTrait,
<D as IntoDiscriminant>::Discriminant: crate::traits::definition::NetabaseDiscriminant,
{
pub fn db(&self) -> &Database {
&self.db
}
#[allow(dead_code)]
pub(crate) fn db_arc(&self) -> &Arc<Database> {
&self.db
}
#[cfg(feature = "redb")]
pub fn tables(&self) -> &D::Tables {
&self.tables
}
}
impl<D> RedbStore<D>
where
D: NetabaseDefinitionTrait + ToIVec,
<D as IntoDiscriminant>::Discriminant: crate::traits::definition::NetabaseDiscriminant,
{
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, NetabaseError> {
let db = Database::create(path)?;
Ok(Self {
db: Arc::new(db),
#[cfg(feature = "redb")]
tables: D::tables(),
trees: D::Discriminant::iter().collect(),
_phantom: PhantomData,
})
}
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, NetabaseError> {
let db = Database::open(path)?;
Ok(Self {
db: Arc::new(db),
#[cfg(feature = "redb")]
tables: D::tables(),
trees: D::Discriminant::iter().collect(),
_phantom: PhantomData,
})
}
pub fn open_tree<M>(&self) -> RedbStoreTree<'_, D, M>
where
M: crate::traits::model::NetabaseModelTrait<D> + std::fmt::Debug + bincode::Decode<()>,
M::Keys: std::fmt::Debug + bincode::Decode<()> + Ord + PartialEq,
{
RedbStoreTree::new(Arc::clone(&self.db), M::DISCRIMINANT)
}
pub fn tree_names(&self) -> Vec<D::Discriminant> {
D::Discriminant::iter().collect()
}
pub fn check_integrity(&mut self) -> Result<bool, NetabaseError> {
let db = Arc::get_mut(&mut self.db).ok_or_else(|| {
NetabaseError::Storage(
"Cannot check integrity: database has multiple references".to_string(),
)
})?;
Ok(db.check_integrity()?)
}
pub fn compact(&mut self) -> Result<bool, NetabaseError> {
let db = Arc::get_mut(&mut self.db).ok_or_else(|| {
NetabaseError::Storage("Cannot compact: database has multiple references".to_string())
})?;
Ok(db.compact()?)
}
}
impl<D> BackendStore<D> for RedbStore<D>
where
D: NetabaseDefinitionTrait + ToIVec,
<D as IntoDiscriminant>::Discriminant: crate::traits::definition::NetabaseDiscriminant,
{
type Config = FileConfig;
fn new(config: Self::Config) -> Result<Self, NetabaseError> {
if config.truncate && config.path.exists() {
std::fs::remove_dir_all(&config.path)?;
}
let db = Database::create(&config.path)?;
Ok(Self {
db: Arc::new(db),
#[cfg(feature = "redb")]
tables: D::tables(),
trees: D::Discriminant::iter().collect(),
_phantom: PhantomData,
})
}
fn open(config: Self::Config) -> Result<Self, NetabaseError> {
let db = Database::open(&config.path)?;
Ok(Self {
db: Arc::new(db),
#[cfg(feature = "redb")]
tables: D::tables(),
trees: D::Discriminant::iter().collect(),
_phantom: PhantomData,
})
}
fn temp() -> Result<Self, NetabaseError> {
let config = FileConfig::temp();
<Self as BackendStore<D>>::new(config)
}
}
impl<D> PathBasedBackend<D> for RedbStore<D>
where
D: NetabaseDefinitionTrait + ToIVec,
<D as IntoDiscriminant>::Discriminant: crate::traits::definition::NetabaseDiscriminant,
{
fn at_path<P: AsRef<Path>>(path: P) -> Result<Self, NetabaseError> {
let config = FileConfig::new(path.as_ref());
<Self as BackendStore<D>>::open(config)
}
}