#![deny(
missing_docs,
non_camel_case_types,
non_snake_case,
path_statements,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_allocation,
unused_import_braces,
unused_imports,
unused_must_use,
unused_mut,
while_true,
clippy::panic,
clippy::print_stdout,
clippy::todo,
//clippy::unwrap_used, // not yet in stable
clippy::wrong_pub_self_convention
)]
#![warn(clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
pub mod backend;
pub mod deser;
pub mod error;
pub use crate::deser::DeSerializer;
use std::fmt::Debug;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde::de::DeserializeOwned;
use serde::Serialize;
#[cfg(feature = "mmap")]
use crate::backend::MmapStorage;
use crate::backend::{Backend, FileBackend, MemoryBackend, PathBackend};
pub use crate::error::*;
#[derive(Debug)]
pub struct Database<Data, Back, DeSer> {
data: RwLock<Data>,
backend: Mutex<Back>,
deser: DeSer,
}
impl<Data, Back, DeSer> Database<Data, Back, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn write<T, R>(&self, task: T) -> error::Result<R>
where
T: FnOnce(&mut Data) -> R,
{
let mut lock = self.data.write().map_err(|_| Error::Poison)?;
Ok(task(&mut lock))
}
pub fn write_safe<T>(&self, task: T) -> error::Result<()>
where
T: FnOnce(&mut Data) + std::panic::UnwindSafe,
{
let mut lock = self.data.write().map_err(|_| Error::Poison)?;
let mut data = lock.clone();
std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
task(&mut data);
}))
.map_err(|_| Error::WritePanic)?;
*lock = data;
Ok(())
}
pub fn read<T, R>(&self, task: T) -> error::Result<R>
where
T: FnOnce(&Data) -> R,
{
let mut lock = self.data.read().map_err(|_| Error::Poison)?;
Ok(task(&mut lock))
}
pub fn borrow_data<'a>(&'a self) -> error::Result<RwLockReadGuard<'a, Data>> {
self.data.read().map_err(|_| Error::Poison)
}
pub fn borrow_data_mut<'a>(&'a self) -> error::Result<RwLockWriteGuard<'a, Data>> {
self.data.write().map_err(|_| Error::Poison)
}
fn load_from_backend(backend: &mut Back, deser: &DeSer) -> error::Result<Data> {
let new_data = deser.deserialize(&backend.get_data()?[..])?;
Ok(new_data)
}
fn load_get_data_lock(&self) -> error::Result<RwLockWriteGuard<'_, Data>> {
let mut backend_lock = self.backend.lock().map_err(|_| Error::Poison)?;
let fresh_data = Self::load_from_backend(&mut backend_lock, &self.deser)?;
drop(backend_lock);
let mut data_write_lock = self.data.write().map_err(|_| Error::Poison)?;
*data_write_lock = fresh_data;
Ok(data_write_lock)
}
pub fn load(&self) -> error::Result<()> {
self.load_get_data_lock().map(|_| ())
}
fn save_data_locked<L: Deref<Target = Data>>(&self, lock: L) -> error::Result<()> {
let ser = self.deser.serialize(lock.deref())?;
drop(lock);
let mut backend = self.backend.lock().map_err(|_| Error::Poison)?;
backend.put_data(&ser)?;
Ok(())
}
pub fn save(&self) -> error::Result<()> {
let data = self.data.read().map_err(|_| Error::Poison)?;
self.save_data_locked(data)
}
pub fn get_data(&self, load: bool) -> error::Result<Data> {
let data = if load {
self.load_get_data_lock()?
} else {
self.data.write().map_err(|_| Error::Poison)?
};
Ok(data.clone())
}
pub fn put_data(&self, new_data: Data, save: bool) -> error::Result<()> {
let mut data = self.data.write().map_err(|_| Error::Poison)?;
*data = new_data;
if save {
self.save_data_locked(data)
} else {
Ok(())
}
}
pub fn from_parts(data: Data, backend: Back, deser: DeSer) -> Self {
Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
}
}
pub fn into_inner(self) -> error::Result<(Data, Back, DeSer)> {
Ok((
self.data.into_inner().map_err(|_| Error::Poison)?,
self.backend
.into_inner()
.map_err(|_| Error::Poison)?,
self.deser,
))
}
pub fn try_clone(&self) -> error::Result<MemoryDatabase<Data, DeSer>> {
let lock = self.data.read().map_err(|_| Error::Poison)?;
Ok(Database {
data: RwLock::new(lock.clone()),
backend: Mutex::new(MemoryBackend::new()),
deser: self.deser.clone(),
})
}
}
pub type FileDatabase<D, DS> = Database<D, FileBackend, DS>;
impl<Data, DeSer> Database<Data, FileBackend, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn load_from_path<S>(path: S) -> error::Result<Self>
where
S: AsRef<std::path::Path>,
{
let mut backend = FileBackend::from_path_or_fail(path)?;
let deser = DeSer::default();
let data = Self::load_from_backend(&mut backend, &deser)?;
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
pub fn load_from_path_or<S>(path: S, data: Data) -> error::Result<Self>
where
S: AsRef<std::path::Path>,
{
let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
if exists {
db.load()?;
}
Ok(db)
}
pub fn load_from_path_or_else<S, C>(path: S, closure: C) -> error::Result<Self>
where
S: AsRef<std::path::Path>,
C: FnOnce() -> Data,
{
let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
let deser = DeSer::default();
let data = if exists {
Self::load_from_backend(&mut backend, &deser)?
} else {
let data = closure();
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
data
};
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
pub fn create_at_path<S>(path: S, data: Data) -> error::Result<Self>
where
S: AsRef<std::path::Path>,
{
let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
pub fn from_file(file: std::fs::File, data: Data) -> error::Result<Self> {
let backend = FileBackend::from_file(file);
Ok(Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser: DeSer::default(),
})
}
}
impl<Data, DeSer> Database<Data, FileBackend, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn load_from_path_or_default<S>(path: S) -> error::Result<Self>
where
S: AsRef<std::path::Path>,
{
Self::load_from_path_or_else(path, Data::default)
}
}
pub type PathDatabase<D, DS> = Database<D, PathBackend, DS>;
impl<Data, DeSer> Database<Data, PathBackend, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn load_from_path(path: PathBuf) -> error::Result<Self> {
let mut backend = PathBackend::from_path_or_fail(path)?;
let deser = DeSer::default();
let data = Self::load_from_backend(&mut backend, &deser)?;
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
pub fn load_from_path_or(path: PathBuf, data: Data) -> error::Result<Self> {
let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
if exists {
db.load()?;
}
Ok(db)
}
pub fn load_from_path_or_else<C>(path: PathBuf, closure: C) -> error::Result<Self>
where
C: FnOnce() -> Data,
{
let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
let deser = DeSer::default();
let data = if exists {
Self::load_from_backend(&mut backend, &deser)?
} else {
let data = closure();
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
data
};
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
pub fn create_at_path(path: PathBuf, data: Data) -> error::Result<Self> {
let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
let db = Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser,
};
Ok(db)
}
}
impl<Data, DeSer> Database<Data, PathBackend, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn load_from_path_or_default(path: PathBuf) -> error::Result<Self> {
Self::load_from_path_or_else(path, Data::default)
}
}
pub type MemoryDatabase<D, DS> = Database<D, MemoryBackend, DS>;
impl<Data, DeSer> Database<Data, MemoryBackend, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn memory(data: Data) -> error::Result<Self> {
let backend = MemoryBackend::new();
Ok(Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser: DeSer::default(),
})
}
}
#[cfg(feature = "mmap")]
pub type MmapDatabase<D, DS> = Database<D, MmapStorage, DS>;
#[cfg(feature = "mmap")]
impl<Data, DeSer> Database<Data, MmapStorage, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn mmap(data: Data) -> error::Result<Self> {
let backend = MmapStorage::new()?;
Ok(Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser: DeSer::default(),
})
}
pub fn mmap_with_size(data: Data, size: usize) -> error::Result<Self> {
let backend = MmapStorage::with_size(size)?;
Ok(Self {
data: RwLock::new(data),
backend: Mutex::new(backend),
deser: DeSer::default(),
})
}
}
impl<Data, Back, DeSer> Database<Data, Back, DeSer> {
pub fn with_deser<T>(self, deser: T) -> Database<Data, Back, T> {
Database {
backend: self.backend,
data: self.data,
deser,
}
}
}
impl<Data, Back, DeSer> Database<Data, Back, DeSer> {
pub fn with_backend<T>(self, backend: T) -> Database<Data, T, DeSer> {
Database {
backend: Mutex::new(backend),
data: self.data,
deser: self.deser,
}
}
}
impl<Data, Back, DeSer> Database<Data, Back, DeSer>
where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
{
pub fn convert_data<C, OutputData>(
self,
convert: C,
) -> error::Result<Database<OutputData, Back, DeSer>>
where
OutputData: Serialize + DeserializeOwned + Clone + Send,
C: FnOnce(Data) -> OutputData,
DeSer: DeSerializer<OutputData> + Send + Sync,
{
let (data, backend, deser) = self.into_inner()?;
Ok(Database {
data: RwLock::new(convert(data)),
backend: Mutex::new(backend),
deser,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use tempfile::NamedTempFile;
type TestData = HashMap<usize, String>;
type TestDb<B> = Database<TestData, B, crate::deser::Ron>;
type TestMemDb = TestDb<MemoryBackend>;
fn test_data() -> TestData {
let mut data = HashMap::new();
data.insert(1, "Hello World".to_string());
data.insert(100, "Rustbreak".to_string());
data
}
#[derive(Clone, Debug, Serialize, serde::Deserialize)]
struct PanicDefault;
impl Default for PanicDefault {
fn default() -> Self {
panic!("`default` was called but should not")
}
}
#[test]
fn create_db_and_read() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn write_twice() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
db.write(|d| d.insert(3, "Write to db".to_string()))
.expect("Rustbreak write error");
db.write(|d| d.insert(3, "Second write".to_string()))
.expect("Rustbreak write error");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Second write",
db.read(|d| d.get(&3).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn save_load() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
db.save().expect("Rustbreak save error");
db.write(|d| d.clear()).expect("Rustbreak write error");
db.load().expect("Rustbreak load error");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn writesafe_twice() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
db.write_safe(|d| {
d.insert(3, "Write to db".to_string());
})
.expect("Rustbreak write error");
db.write_safe(|d| {
d.insert(3, "Second write".to_string());
})
.expect("Rustbreak write error");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Second write",
db.read(|d| d.get(&3).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn writesafe_panic() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
let err = db
.write_safe(|d| {
d.clear();
panic!("Panic should be catched")
})
.expect_err("Did not error on panic in safe write!");
assert!(matches!(err, Error::WritePanic));
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn borrow_data_twice() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
let readlock1 = db.borrow_data().expect("Rustbreak readlock error");
let readlock2 = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(
"Hello World",
readlock1.get(&1).expect("Should be `Some` but was `None`")
);
assert_eq!(
"Hello World",
readlock2.get(&1).expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
readlock1
.get(&100)
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
readlock2
.get(&100)
.expect("Should be `Some` but was `None`")
);
assert_eq!(*readlock1, *readlock2);
}
#[test]
fn borrow_data_mut() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
let mut writelock = db.borrow_data_mut().expect("Rustbreak writelock error");
writelock.insert(3, "Write to db".to_string());
drop(writelock);
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Write to db",
db.read(|d| d.get(&3).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
}
#[test]
fn get_data_mem() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
let data = db.get_data(false).expect("could not get data");
assert_eq!(test_data(), data);
}
#[test]
fn get_data_load() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
db.save().expect("Rustbreak save error");
db.write(|d| d.clear()).expect("Rustbreak write error");
let data = db.get_data(true).expect("could not get data");
assert_eq!(test_data(), data);
}
#[test]
fn put_data_mem() {
let db = TestMemDb::memory(TestData::default()).expect("Could not create database");
db.put_data(test_data(), false).expect("could not put data");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
let data = db.get_data(false).expect("could not get data");
assert_eq!(test_data(), data);
}
#[test]
fn put_data_save() {
let db = TestMemDb::memory(TestData::default()).expect("Could not create database");
db.put_data(test_data(), true).expect("could not put data");
db.load().expect("Rustbreak load error");
assert_eq!(
"Hello World",
db.read(|d| d.get(&1).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
assert_eq!(
"Rustbreak",
db.read(|d| d.get(&100).cloned())
.expect("Rustbreak read error")
.expect("Should be `Some` but was `None`")
);
let data = db.get_data(false).expect("could not get data");
assert_eq!(test_data(), data);
}
#[test]
fn save_and_into_inner() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
db.save().expect("Rustbreak save error");
let (data, mut backend, _) = db
.into_inner()
.expect("error calling `Database.into_inner`");
assert_eq!(test_data(), data);
let parsed: TestData =
ron::de::from_reader(&backend.get_data().expect("could not get data from backend")[..])
.expect("backend contains invalid RON");
assert_eq!(test_data(), parsed);
}
#[test]
fn clone() {
let db1 = TestMemDb::memory(test_data()).expect("Could not create database");
let readlock1 = db1.borrow_data().expect("Rustbreak readlock error");
let db2 = db1.try_clone().expect("Rustbreak clone error");
let readlock2 = db2.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock1);
assert_eq!(*readlock1, *readlock2);
}
#[test]
fn allow_databases_with_boxed_backend() {
let db =
MemoryDatabase::<Vec<u64>, crate::deser::Ron>::memory(vec![]).expect("To be created");
let db: Database<_, Box<dyn Backend>, _> = db.with_backend(Box::new(MemoryBackend::new()));
db.put_data(vec![1, 2, 3], true)
.expect("Can save data in memory");
assert_eq!(
&[1, 2, 3],
&db.get_data(true).expect("Can get data from memory")[..]
);
}
#[test]
fn save_holding_readlock() {
let db = TestMemDb::memory(test_data()).expect("Could not create database");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
db.save().expect("Rustbreak save error");
assert_eq!(test_data(), *readlock);
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_or_else_existing_nocall() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path().to_owned();
let _ = TestDb::<PathBackend>::load_from_path_or_else(path, || {
panic!("closure called while file existed")
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_or_else_existing_nocall() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path();
let _ = TestDb::<FileBackend>::load_from_path_or_else(path, || {
panic!("closure called while file existed")
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_or_default_existing_nocall() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path().to_owned();
let _ = Database::<PanicDefault, PathBackend, crate::deser::Ron>::load_from_path_or_default(
path,
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_or_default_existing_nocall() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path();
let _ = Database::<PanicDefault, FileBackend, crate::deser::Ron>::load_from_path_or_default(
path,
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_or_new() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let db = TestDb::<PathBackend>::load_from_path_or(file_path, test_data())
.expect("could not load from path");
db.load().expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_or_else_new() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let db = TestDb::<PathBackend>::load_from_path_or_else(file_path, test_data)
.expect("could not load from path");
db.load().expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_or_new() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let db = TestDb::<FileBackend>::load_from_path_or(file_path, test_data())
.expect("could not load from path");
db.load().expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_or_else_new() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let db = TestDb::<FileBackend>::load_from_path_or_else(file_path, test_data)
.expect("could not load from path");
db.load().expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_new_fail() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let err = TestDb::<PathBackend>::load_from_path(file_path)
.expect_err("should fail with file not found");
if let Error::Backend(BackendError::Io(io_err)) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong error: {}", err)
};
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_new_fail() {
let dir = tempfile::tempdir().expect("could not create temporary directory");
let mut file_path = dir.path().to_owned();
file_path.push("rustbreak_path_db.db");
let err = TestDb::<FileBackend>::load_from_path(file_path)
.expect_err("should fail with file not found");
if let Error::Backend(BackendError::Io(io_err)) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong error: {}", err)
};
dir.close().expect("Error while deleting temp directory!");
}
#[test]
#[cfg_attr(miri, ignore)]
fn pathdb_from_path_existing() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path().to_owned();
let db = TestDb::<PathBackend>::create_at_path(path.clone(), test_data())
.expect("could not create db");
db.save().expect("could not save db");
drop(db);
let db = TestDb::<PathBackend>::load_from_path(path).expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
}
#[test]
#[cfg_attr(miri, ignore)]
fn filedb_from_path_existing() {
let file = NamedTempFile::new().expect("could not create temporary file");
let path = file.path();
let db =
TestDb::<FileBackend>::create_at_path(path, test_data()).expect("could not create db");
db.save().expect("could not save db");
drop(db);
let db = TestDb::<FileBackend>::load_from_path(path).expect("could not load");
let readlock = db.borrow_data().expect("Rustbreak readlock error");
assert_eq!(test_data(), *readlock);
}
}