use crate::config::Config;
use crate::db::{DB, DBInner};
use crate::error::Result;
#[cfg(feature = "aof")]
use crate::storage::AOFFile;
#[cfg(feature = "snapshot")]
use crate::storage::SnapshotConfig;
#[cfg(feature = "snapshot")]
use crate::storage::SnapshotFile;
use std::path::PathBuf;
#[derive(Debug)]
pub struct DBBuilder {
#[cfg(feature = "aof")]
aof_path: Option<PathBuf>,
#[cfg(feature = "snapshot")]
snapshot_path: Option<PathBuf>,
config: Config,
in_memory: bool,
}
impl DBBuilder {
pub fn new() -> Self {
Self {
#[cfg(feature = "aof")]
aof_path: None,
#[cfg(feature = "snapshot")]
snapshot_path: None,
config: Config::default(),
in_memory: true,
}
}
#[cfg(feature = "aof")]
pub fn aof_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.aof_path = Some(path.into());
self.in_memory = false;
self
}
#[cfg(feature = "snapshot")]
pub fn snapshot_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.snapshot_path = Some(path.into());
self.in_memory = false;
self
}
pub fn in_memory(mut self) -> Self {
self.in_memory = true;
#[cfg(feature = "aof")]
{
self.aof_path = None;
}
#[cfg(feature = "snapshot")]
{
self.snapshot_path = None;
}
self
}
pub fn config(mut self, config: Config) -> Self {
self.config = config;
self
}
#[cfg(feature = "time-index")]
pub fn history_capacity(mut self, capacity: usize) -> Self {
self.config = self.config.clone().with_history_capacity(capacity);
self
}
pub fn build(self) -> Result<DB> {
let mut inner = DBInner::new_with_config(&self.config);
if !self.in_memory {
#[cfg(feature = "aof")]
if let Some(aof_path) = self.aof_path {
let mut aof_file = AOFFile::open(&aof_path)?;
inner.load_from_aof(&mut aof_file)?;
inner.aof_file = Some(aof_file);
}
#[cfg(feature = "snapshot")]
if let Some(snapshot_path) = self.snapshot_path {
let snapshot_config = SnapshotConfig {
auto_snapshot_ops: self.config.snapshot_auto_ops,
};
let snapshot_file = SnapshotFile::new(&snapshot_path, snapshot_config);
inner.load_from_snapshot(&snapshot_file)?;
inner.snapshot_file = Some(snapshot_file);
}
}
Ok(DB {
inner,
#[cfg(not(feature = "sync"))]
_not_send_sync: std::marker::PhantomData,
})
}
}
impl Default for DBBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "time-index")]
use crate::config::HistoryEventKind;
use crate::config::SyncPolicy;
use std::time::Duration;
#[test]
fn test_builder_default() {
let builder = DBBuilder::new();
assert!(builder.in_memory);
}
#[test]
fn test_builder_in_memory() {
let mut db = DBBuilder::new().in_memory().build().unwrap();
db.insert("test", b"value", None).unwrap();
assert_eq!(db.get("test").unwrap().unwrap().as_ref(), b"value");
}
#[test]
fn test_builder_with_config() {
let config = Config::default()
.with_sync_policy(SyncPolicy::Always)
.with_default_ttl(Duration::from_secs(3600));
let mut db = DBBuilder::new().config(config).build().unwrap();
db.insert("test", b"value", None).unwrap();
}
#[cfg(feature = "time-index")]
#[test]
fn test_builder_history_capacity() {
let mut db = DBBuilder::new().history_capacity(2).build().unwrap();
db.insert("key", b"v1", None).unwrap();
db.insert("key", b"v2", None).unwrap();
db.delete("key").unwrap();
let history = db.history("key").unwrap();
assert_eq!(history.len(), 2);
assert_eq!(history[0].kind, HistoryEventKind::Set);
assert_eq!(history[1].kind, HistoryEventKind::Delete);
}
#[cfg(feature = "aof")]
#[test]
fn test_builder_aof_path() {
let temp_dir = std::env::temp_dir();
let aof_path = temp_dir.join("test_builder.aof");
let _ = std::fs::remove_file(&aof_path);
let mut db = DBBuilder::new().aof_path(&aof_path).build().unwrap();
db.insert("persistent", b"data", None).unwrap();
drop(db);
let db2 = DBBuilder::new().aof_path(&aof_path).build().unwrap();
assert_eq!(db2.get("persistent").unwrap().unwrap().as_ref(), b"data");
let _ = std::fs::remove_file(aof_path);
}
#[cfg(feature = "aof")]
#[test]
fn test_builder_aof_path_disables_in_memory() {
let temp_dir = std::env::temp_dir();
let aof_path = temp_dir.join("test_builder2.aof");
let _ = std::fs::remove_file(&aof_path);
let builder = DBBuilder::new().in_memory().aof_path(&aof_path);
assert!(!builder.in_memory);
assert!(builder.aof_path.is_some());
let _ = std::fs::remove_file(aof_path);
}
#[cfg(feature = "aof")]
#[test]
fn test_builder_in_memory_clears_aof_path() {
let temp_dir = std::env::temp_dir();
let aof_path = temp_dir.join("test_builder3.aof");
let builder = DBBuilder::new().aof_path(aof_path).in_memory();
assert!(builder.in_memory);
assert!(builder.aof_path.is_none());
}
}