use crate::{
config::Config,
error::{OpenError, OpenMemoryError, PE},
persy::PersyImpl,
Persy, Recover,
};
use std::{fs, fs::File, path::Path, sync::Arc};
pub struct OpenOptions {
truncate: bool,
create: bool,
create_new: bool,
config: Config,
prepare: Option<Box<dyn Fn(&Persy) -> Result<(), Box<dyn std::error::Error>>>>,
recover: Option<Box<dyn Fn(&Vec<u8>) -> bool>>,
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {
truncate: false,
create: false,
create_new: false,
config: Config::new(),
prepare: None,
recover: None,
}
}
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
self.truncate = truncate;
self
}
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
self.create = create;
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
self.create_new = create_new;
self
}
pub fn prepare_with<F>(&mut self, prepare: F) -> &mut OpenOptions
where
F: Fn(&Persy) -> Result<(), Box<dyn std::error::Error>> + 'static,
{
self.prepare = Some(Box::new(prepare));
self
}
pub fn recover_with<F>(&mut self, recover: F) -> &mut OpenOptions
where
F: Fn(&Vec<u8>) -> bool + 'static,
{
self.recover = Some(Box::new(recover));
self
}
pub fn config(&mut self, config: Config) -> &mut OpenOptions {
self.config = config;
self
}
pub fn recover<P>(&mut self, path: P) -> Result<Recover, PE<OpenError>>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let exists = path.exists();
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(self.create)
.create_new(self.create_new)
.open(path)?;
self.int_recover_file(file, exists)
}
pub fn recover_file(&mut self, file: File) -> Result<Recover, PE<OpenError>> {
self.int_recover_file(file, true)
}
fn int_recover_file(&mut self, file: File, exists: bool) -> Result<Recover, PE<OpenError>> {
let must_prepare = !exists || self.truncate;
let config = self.config.clone();
if must_prepare {
let (persy_impl, recov) = if self.truncate {
PersyImpl::truncate_and_open(file, config)?
} else {
PersyImpl::create_and_open(file, config)?
};
let p = Arc::new(persy_impl);
if let Some(prepare) = &mut self.prepare {
let persy = Persy { persy_impl: p.clone() };
(prepare)(&persy).map_err(|e| OpenError::InitError(format!("{}", e)))?;
}
Ok(Recover::new(recov, p))
} else if let Some(recover) = &self.recover {
let (persy_impl, mut recov) = PersyImpl::open_recover(file, config)?;
recov.apply(recover)?;
Ok(Recover::new(recov, Arc::new(persy_impl)))
} else {
let (persy_impl, recov) = PersyImpl::open_recover(file, config)?;
Ok(Recover::new(recov, Arc::new(persy_impl)))
}
}
pub fn open<P>(&mut self, path: P) -> Result<Persy, PE<OpenError>>
where
P: AsRef<Path>,
{
let recover = self.recover(path)?;
recover.finalize().map_err(|e| PE::PE(OpenError::from(e.error())))
}
pub fn memory(&mut self) -> Result<Persy, PE<OpenMemoryError>> {
let config = self.config.clone();
let persy_impl = PersyImpl::memory(config)?;
let persy = Persy {
persy_impl: Arc::new(persy_impl),
};
if let Some(prepare) = &mut self.prepare {
(prepare)(&persy).map_err(|e| OpenMemoryError::InitError(format!("{}", e)))?;
}
Ok(persy)
}
}
impl Default for OpenOptions {
fn default() -> OpenOptions {
OpenOptions::new()
}
}