use std::ops::Deref;
use std::ops::DerefMut;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, MutexGuard};
use serde::Serialize;
use serde::de::DeserializeOwned;
use errors::*;
use helpers::*;
pub struct Mvdb<T> {
inner: Arc<Mutex<T>>,
file_path: PathBuf,
pretty: bool,
}
impl<T> Clone for Mvdb<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
file_path: self.file_path.clone(),
pretty: self.pretty,
}
}
}
impl<T> Mvdb<T>
where
T: Serialize + DeserializeOwned,
{
pub fn new(data: T, path: &Path) -> Result<Self> {
Self::new_inner(data, path, false)
}
pub fn new_pretty(data: T, path: &Path) -> Result<Self> {
Self::new_inner(data, path, true)
}
pub fn from_file(path: &Path) -> Result<Self> {
Self::from_file_inner(path, false)
}
pub fn from_file_pretty(path: &Path) -> Result<Self> {
Self::from_file_inner(path, true)
}
fn new_inner(data: T, path: &Path, pretty: bool) -> Result<Self> {
let new_self = Self::new_no_write(data, path, pretty);
new_self.write()?;
Ok(new_self)
}
fn from_file_inner(path: &Path, pretty: bool) -> Result<Self> {
let contents = just_load(&path)?;
Ok(Self::new_no_write(contents, path, pretty))
}
fn new_no_write(data: T, path: &Path, pretty: bool) -> Self {
Self {
inner: Arc::new(Mutex::new(data)),
file_path: path.to_path_buf(),
pretty: pretty,
}
}
pub fn access<F, R>(&self, action: F) -> Result<R>
where
F: Fn(&T) -> R,
{
let x = self.lock()?;
let y = x.deref();
Ok(action(y))
}
pub fn access_mut<F, R>(&self, action: F) -> Result<R>
where
F: FnOnce(&mut T) -> R,
{
let mut x = self.lock()?;
let mut y = x.deref_mut();
let (_, hash_before) = hash_by_serialize(&y, self.pretty)?;
let ret = action(y);
let (ser, hash_after) = hash_by_serialize(&y, self.pretty)?;
if hash_before != hash_after {
just_write_string(&ser, &self.file_path)?;
}
Ok(ret)
}
fn write(&self) -> Result<()> {
if let Ok(inner) = self.inner.lock() {
self.write_locked(&inner.deref())
} else {
bail!("Failed to write")
}
}
fn write_locked(&self, inner: &T) -> Result<()> {
just_write(&inner.deref(), &self.file_path, self.pretty)
}
fn lock(&self) -> Result<MutexGuard<T>> {
match self.inner.lock() {
Err(_) => bail!("failed to lock"),
Ok(lock) => Ok(lock),
}
}
}
impl<T> Mvdb<T>
where
T: Serialize + DeserializeOwned + Default,
{
pub fn from_file_or_default(path: &Path) -> Result<Self> {
Self::from_file_or_default_inner(path, false)
}
pub fn from_file_or_default_pretty(path: &Path) -> Result<Self> {
Self::from_file_or_default_inner(path, true)
}
fn from_file_or_default_inner(path: &Path, pretty: bool) -> Result<Self> {
match just_load(path) {
Ok(data) => Ok(Self::new_no_write(data, path, pretty)),
Err(_) => Self::new_inner(T::default(), path, pretty),
}
}
}