use std::path::Path;
use super::config::BfTreeConfig;
use super::database::BfTreeDatabase;
use super::error::BfTreeError;
pub enum BackendChoice {
Legacy,
BfTree(BfTreeConfig),
}
impl Default for BackendChoice {
fn default() -> Self {
Self::Legacy
}
}
pub enum UnifiedDatabase {
Legacy(crate::Database),
BfTree(BfTreeDatabase),
}
impl UnifiedDatabase {
#[cfg(feature = "std")]
pub fn create(backend: BackendChoice, path: impl AsRef<Path>) -> Result<Self, UnifiedError> {
match backend {
BackendChoice::Legacy => {
let db = crate::Database::create(path)?;
Ok(Self::Legacy(db))
}
BackendChoice::BfTree(config) => {
let db = BfTreeDatabase::create(config)?;
Ok(Self::BfTree(db))
}
}
}
#[cfg(feature = "std")]
pub fn open(backend: BackendChoice, path: impl AsRef<Path>) -> Result<Self, UnifiedError> {
match backend {
BackendChoice::Legacy => {
let db = crate::Database::open(path)?;
Ok(Self::Legacy(db))
}
BackendChoice::BfTree(config) => {
let db = BfTreeDatabase::open(config)?;
Ok(Self::BfTree(db))
}
}
}
pub fn is_legacy(&self) -> bool {
matches!(self, Self::Legacy(_))
}
pub fn is_bf_tree(&self) -> bool {
matches!(self, Self::BfTree(_))
}
pub fn as_legacy(&self) -> Option<&crate::Database> {
match self {
Self::Legacy(db) => Some(db),
Self::BfTree(_) => None,
}
}
pub fn as_bf_tree(&self) -> Option<&BfTreeDatabase> {
match self {
Self::BfTree(db) => Some(db),
Self::Legacy(_) => None,
}
}
pub fn into_legacy(self) -> Option<crate::Database> {
match self {
Self::Legacy(db) => Some(db),
Self::BfTree(_) => None,
}
}
pub fn into_bf_tree(self) -> Option<BfTreeDatabase> {
match self {
Self::BfTree(db) => Some(db),
Self::Legacy(_) => None,
}
}
}
#[derive(Debug)]
pub enum UnifiedError {
Legacy(crate::DatabaseError),
BfTree(BfTreeError),
}
impl core::fmt::Display for UnifiedError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Legacy(e) => write!(f, "legacy: {e}"),
Self::BfTree(e) => write!(f, "bftree: {e}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnifiedError {}
impl From<crate::DatabaseError> for UnifiedError {
fn from(e: crate::DatabaseError) -> Self {
Self::Legacy(e)
}
}
impl From<BfTreeError> for UnifiedError {
fn from(e: BfTreeError) -> Self {
Self::BfTree(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TableDefinition;
use crate::db::ReadableDatabase;
const TABLE: TableDefinition<&str, u64> = TableDefinition::new("unified_test");
#[test]
fn unified_bftree_create_and_use() {
let config = BfTreeConfig::new_memory(4);
let udb = UnifiedDatabase::create(BackendChoice::BfTree(config), "").unwrap();
assert!(udb.is_bf_tree());
assert!(!udb.is_legacy());
let db = udb.as_bf_tree().unwrap();
let wtxn = db.begin_write();
let mut t = wtxn.open_table(TABLE).unwrap();
t.insert(&"hello", &42u64).unwrap();
drop(t);
wtxn.commit().unwrap();
let rtxn = db.begin_read();
let mut t = rtxn.open_table(TABLE).unwrap();
let val = t.get(&"hello").unwrap().unwrap();
assert_eq!(u64::from_le_bytes(val[..8].try_into().unwrap()), 42);
}
#[test]
fn unified_legacy_create_and_use() {
let tmp = tempfile::NamedTempFile::new().unwrap();
let udb = UnifiedDatabase::create(BackendChoice::Legacy, tmp.path()).unwrap();
assert!(udb.is_legacy());
let db = udb.as_legacy().unwrap();
let wtxn = db.begin_write().unwrap();
{
let mut t = wtxn.open_table(TABLE).unwrap();
t.insert("hello", &42u64).unwrap();
}
wtxn.commit().unwrap();
let rtxn = db.begin_read().unwrap();
let t = rtxn.open_table(TABLE).unwrap();
assert_eq!(t.get("hello").unwrap().unwrap().value(), 42);
}
#[test]
fn backend_choice_default_is_legacy() {
assert!(matches!(BackendChoice::default(), BackendChoice::Legacy));
}
#[test]
fn into_conversions() {
let config = BfTreeConfig::new_memory(4);
let udb = UnifiedDatabase::create(BackendChoice::BfTree(config), "").unwrap();
assert!(udb.into_legacy().is_none());
let config2 = BfTreeConfig::new_memory(4);
let udb2 = UnifiedDatabase::create(BackendChoice::BfTree(config2), "").unwrap();
assert!(udb2.into_bf_tree().is_some());
}
}