use crate::*;
use std::path::{Path, PathBuf};
use std::fs;
#[macro_export]
macro_rules! search_database {
(($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *) => {(|| {
let database = $ldb;
let container = database.as_container()?;
$(
$(let container = container.read_container(stringify!($con))?;)?
$(let container = container.read_container($can)?;)?
)*
let result: Result<LazyContainer, LDBError> = Ok(container);
result
})()};
(($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))?) => {(|| {
let container = search_database!(($ldb) /$($($con)?$(($can))?)/ *)?;
$(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
$(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
result
})()};
(($ldb:expr) $($item:ident)?$(($obj:expr))?) => {(|| {
let database = $ldb;
let container = database.as_container()?;
$(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
$(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
result
})()};
}
#[macro_export]
macro_rules! write_database {
(($ldb:expr) $($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
let database = $ldb;
let container = database.as_container()?;
$(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
$(LazyData::$func(container.data_writer($obj)?, $value)?;)?
Result::<(), LDBError>::Ok(())
})()};
(($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
let database = $ldb;
let mut container = database.as_container()?;
$({
let con = $(stringify!($con))?$($can)?;
container = match container.read_container(con) {
Ok(x) => x,
Err(LDBError::DirNotFound(_)) => container.new_container(con)?,
Err(e) => return Err(e),
}
};)*
$(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
$(LazyData::$func(container.data_writer($obj)?, $value)?;)?
Result::<(), LDBError>::Ok(())
})()}
}
pub struct LazyDB {
path: PathBuf,
compressed: bool,
}
impl LazyDB {
pub fn init(path: impl AsRef<Path>) -> Result<Self, LDBError> {
let path = path.as_ref();
if !path.is_dir() { unwrap_result!((fs::create_dir_all(path)) err => LDBError::IOError(err)) };
{ let meta = path.join(".meta");
if !meta.is_file() {
LazyData::new_binary(
FileWrapper::new_writer(
unwrap_result!((fs::File::create(meta)) err => LDBError::IOError(err))
), &[VERSION.major, VERSION.minor, VERSION.build],
)?;
}
};
Ok(Self {
path: path.to_path_buf(),
compressed: false,
})
}
pub fn init_db(path: impl AsRef<Path>) -> Result<Self, LDBError> {
let dir_path = path.as_ref().with_extension("modb");
let mut this = Self::init(dir_path)?;
this.compressed = true;
Ok(this)
}
pub fn load_dir(path: impl AsRef<Path>) -> Result<Self, LDBError> {
let path = path.as_ref();
if !path.is_dir() { return Err(LDBError::DirNotFound(path.to_path_buf())) };
let meta = path.join(".meta");
if !meta.is_file() { return Err(LDBError::FileNotFound(meta)) };
let read_version = LazyData::load(&meta)?.collect_binary()?;
if read_version.len() != 3 { return Err(LDBError::InvalidMetaVersion(meta)) };
let read_version = version::Version::new(read_version[0], read_version[1], read_version[2]);
if !VERSION.is_compatible(&read_version) { return Err(LDBError::IncompatibleVersion(read_version)) };
Ok(Self {
path: path.to_path_buf(),
compressed: false,
})
}
pub fn load_db(path: impl AsRef<Path>) -> Result<Self, LDBError> {
let path = path.as_ref();
{ let dir_path = path.with_extension("modb");
if dir_path.is_dir() { return Self::load_dir(dir_path) }
}
let path = Self::decompile(path)?;
let mut ldb = Self::load_dir(path)?;
ldb.compressed = true;
Ok(ldb)
}
#[inline]
pub fn as_container(&self) -> Result<LazyContainer, LDBError> {
LazyContainer::load(&self.path)
}
pub fn compile(&self) -> Result<PathBuf, std::io::Error> {
use lazy_archive::*; let tar = self.path.with_extension("tmp.tar");
let new = self.path.with_extension("ldb");
build_tar(&self.path, &tar)?; compress_file(&tar, &new)?;
fs::remove_file(tar)?;
Ok(new)
}
pub fn decompile(path: impl AsRef<Path>) -> Result<PathBuf, LDBError> {
use lazy_archive::*; let path = path.as_ref();
if !path.is_file() { return Err(LDBError::FileNotFound(path.to_path_buf())) };
let tar = path.with_extension("tmp.tar");
let unpacked = path.with_extension("modb");
unwrap_result!((decompress_file(path, &tar)) err => LDBError::IOError(err));
unwrap_result!((unpack_tar(&tar, &unpacked)) err => LDBError::IOError(err));
unwrap_result!((fs::remove_file(tar)) err => LDBError::IOError(err));
Ok(unpacked)
}
}
impl Drop for LazyDB {
fn drop(&mut self) {
if !self.compressed { return }; let ok = self.compile().is_ok();
if !ok { return }; let _ = fs::remove_dir_all(&self.path);
}
}