use std::{
fs,
os::raw::c_uint,
path::{
Path,
PathBuf,
},
};
#[cfg(any(feature = "db-dup-sort", feature = "db-int-key"))]
use crate::backend::{
BackendDatabaseFlags,
DatabaseFlags,
};
use crate::{
backend::{
BackendEnvironment,
BackendEnvironmentBuilder,
BackendRoCursorTransaction,
BackendRwCursorTransaction,
SafeModeError,
},
error::StoreError,
readwrite::{
Reader,
Writer,
},
store::{
single::SingleStore,
Options as StoreOptions,
},
};
#[cfg(feature = "db-dup-sort")]
use crate::store::multi::MultiStore;
#[cfg(feature = "db-int-key")]
use crate::store::integer::IntegerStore;
#[cfg(feature = "db-int-key")]
use crate::store::keys::PrimitiveInt;
#[cfg(all(feature = "db-dup-sort", feature = "db-int-key"))]
use crate::store::integermulti::MultiIntegerStore;
pub static DEFAULT_MAX_DBS: c_uint = 5;
#[derive(Debug)]
pub struct Rkv<E> {
path: PathBuf,
env: E,
}
impl<'e, E> Rkv<E>
where
E: BackendEnvironment<'e>,
{
pub fn environment_builder<B>() -> B
where
B: BackendEnvironmentBuilder<'e, Environment = E>,
{
B::new()
}
#[allow(clippy::new_ret_no_self)]
pub fn new<B>(path: &Path) -> Result<Rkv<E>, StoreError>
where
B: BackendEnvironmentBuilder<'e, Environment = E>,
{
Rkv::with_capacity::<B>(path, DEFAULT_MAX_DBS)
}
pub fn with_capacity<B>(path: &Path, max_dbs: c_uint) -> Result<Rkv<E>, StoreError>
where
B: BackendEnvironmentBuilder<'e, Environment = E>,
{
let mut builder = B::new();
builder.set_max_dbs(max_dbs);
Rkv::from_builder(path, builder)
}
pub fn from_builder<B>(path: &Path, builder: B) -> Result<Rkv<E>, StoreError>
where
B: BackendEnvironmentBuilder<'e, Environment = E>,
{
Ok(Rkv {
path: path.into(),
env: builder.open(path).map_err(|e| e.into())?,
})
}
}
impl<'e, E> Rkv<E>
where
E: BackendEnvironment<'e>,
{
pub fn get_dbs(&self) -> Result<Vec<Option<String>>, StoreError> {
self.env.get_dbs().map_err(|e| e.into())
}
pub fn open_single<'s, T>(
&self,
name: T,
opts: StoreOptions<E::Flags>,
) -> Result<SingleStore<E::Database>, StoreError>
where
T: Into<Option<&'s str>>,
{
self.open(name, opts).map(SingleStore::new)
}
#[cfg(feature = "db-int-key")]
pub fn open_integer<'s, T, K>(
&self,
name: T,
mut opts: StoreOptions<E::Flags>,
) -> Result<IntegerStore<E::Database, K>, StoreError>
where
K: PrimitiveInt,
T: Into<Option<&'s str>>,
{
opts.flags.set(DatabaseFlags::INTEGER_KEY, true);
self.open(name, opts).map(IntegerStore::new)
}
#[cfg(feature = "db-dup-sort")]
pub fn open_multi<'s, T>(
&self,
name: T,
mut opts: StoreOptions<E::Flags>,
) -> Result<MultiStore<E::Database>, StoreError>
where
T: Into<Option<&'s str>>,
{
opts.flags.set(DatabaseFlags::DUP_SORT, true);
self.open(name, opts).map(MultiStore::new)
}
#[cfg(all(feature = "db-dup-sort", feature = "db-int-key"))]
pub fn open_multi_integer<'s, T, K>(
&self,
name: T,
mut opts: StoreOptions<E::Flags>,
) -> Result<MultiIntegerStore<E::Database, K>, StoreError>
where
K: PrimitiveInt,
T: Into<Option<&'s str>>,
{
opts.flags.set(DatabaseFlags::INTEGER_KEY, true);
opts.flags.set(DatabaseFlags::DUP_SORT, true);
self.open(name, opts).map(MultiIntegerStore::new)
}
fn open<'s, T>(&self, name: T, opts: StoreOptions<E::Flags>) -> Result<E::Database, StoreError>
where
T: Into<Option<&'s str>>,
{
if opts.create {
self.env.create_db(name.into(), opts.flags).map_err(|e| {
match e.into() {
StoreError::LmdbError(lmdb::Error::BadRslot) => StoreError::open_during_transaction(),
StoreError::SafeModeError(SafeModeError::DbsIllegalOpen) => StoreError::open_during_transaction(),
e => e,
}
})
} else {
self.env.open_db(name.into()).map_err(|e| {
match e.into() {
StoreError::LmdbError(lmdb::Error::BadRslot) => StoreError::open_during_transaction(),
StoreError::SafeModeError(SafeModeError::DbsIllegalOpen) => StoreError::open_during_transaction(),
e => e,
}
})
}
}
}
impl<'e, E> Rkv<E>
where
E: BackendEnvironment<'e>,
{
pub fn read<T>(&'e self) -> Result<Reader<T>, StoreError>
where
E: BackendEnvironment<'e, RoTransaction = T>,
T: BackendRoCursorTransaction<'e, Database = E::Database>,
{
Ok(Reader::new(self.env.begin_ro_txn().map_err(|e| e.into())?))
}
pub fn write<T>(&'e self) -> Result<Writer<T>, StoreError>
where
E: BackendEnvironment<'e, RwTransaction = T>,
T: BackendRwCursorTransaction<'e, Database = E::Database>,
{
Ok(Writer::new(self.env.begin_rw_txn().map_err(|e| e.into())?))
}
}
impl<'e, E> Rkv<E>
where
E: BackendEnvironment<'e>,
{
pub fn sync(&self, force: bool) -> Result<(), StoreError> {
self.env.sync(force).map_err(|e| e.into())
}
pub fn stat(&self) -> Result<E::Stat, StoreError> {
self.env.stat().map_err(|e| e.into())
}
pub fn info(&self) -> Result<E::Info, StoreError> {
self.env.info().map_err(|e| e.into())
}
pub fn load_ratio(&self) -> Result<Option<f32>, StoreError> {
self.env.load_ratio().map_err(|e| e.into())
}
pub fn set_map_size(&self, size: usize) -> Result<(), StoreError> {
self.env.set_map_size(size).map_err(Into::into)
}
pub fn close_and_delete(self) -> Result<(), StoreError> {
let files = self.env.get_files_on_disk();
self.sync(true)?;
drop(self);
for file in files {
fs::remove_file(file)?;
}
Ok(())
}
}