1use crate::*;
2use std::path::{Path, PathBuf};
3use std::fs;
4
5#[macro_export]
7macro_rules! search_database {
8 (($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *) => {(|| {
9 let database = &$ldb;
10 let container = database.as_container()?;
11 $(
12 $(let container = container.child_container(stringify!($con))?;)?
13 $(let container = container.child_container($can)?;)?
14 )*
15 let result: Result<LazyContainer, LDBError> = Ok(container);
16 result
17 })()};
18
19 (($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))?) => {(|| {
20 let container = search_database!(($ldb) /$($($con)?$(($can))?)/ *)?;
21 $(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
22 $(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
23 result
24 })()};
25
26 (($ldb:expr) $($item:ident)?$(($obj:expr))?) => {(|| {
27 let database = &$ldb;
28 let container = database.as_container()?;
29 $(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
30 $(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
31 result
32 })()};
33}
34
35#[macro_export]
37macro_rules! write_database {
38 (($ldb:expr) $($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
39 let database = &$ldb;
40 let container = database.as_container()?;
41 $(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
42 $(LazyData::$func(container.data_writer($obj)?, $value)?;)?
43 Result::<(), LDBError>::Ok(())
44 })()};
45
46 (($ldb:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
47 let mut container = search_database!(($ldb) /$($($con)?$(($can))?)/ *)?;
48
49 $(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
50 $(LazyData::$func(container.data_writer($obj)?, $value)?;)?
51 Result::<(), LDBError>::Ok(())
52 })()}
53}
54
55pub struct LazyDB {
56 path: PathBuf,
57 compressed: bool,
58}
59
60impl LazyDB {
61 pub fn init(path: impl AsRef<Path>) -> Result<Self, LDBError> {
68 let path = path.as_ref();
69
70 if !path.is_dir() { unwrap_result!((fs::create_dir_all(path)) err => LDBError::IOError(err)) };
72
73 { let meta = path.join(".meta");
75 if !meta.is_file() {
76 LazyData::new_binary(
78 FileWrapper::new_writer(
79 unwrap_result!((fs::File::create(meta)) err => LDBError::IOError(err))
80 ), &[VERSION.major, VERSION.minor, VERSION.build],
81 )?;
82 }
83 };
84
85 Ok(Self {
87 path: path.to_path_buf(),
88 compressed: false,
89 })
90 }
91
92 pub fn init_db(path: impl AsRef<Path>) -> Result<Self, LDBError> {
96 let dir_path = path.as_ref().with_extension("modb");
97 let mut this = Self::init(dir_path)?;
98 this.compressed = true;
99 Ok(this)
100 }
101
102 pub fn load_dir(path: impl AsRef<Path>) -> Result<Self, LDBError> {
108 let path = path.as_ref();
109
110 if !path.is_dir() { return Err(LDBError::DirNotFound(path.to_path_buf())) };
112
113 let meta = path.join(".meta");
115 if !meta.is_file() { return Err(LDBError::FileNotFound(meta)) };
116
117 let read_version = LazyData::load(&meta)?.collect_binary()?;
119 if read_version.len() != 3 { return Err(LDBError::InvalidMetaVersion(meta)) };
120 let read_version = version::Version::new(read_version[0], read_version[1], read_version[2]);
121 if !VERSION.is_compatible(&read_version) { return Err(LDBError::IncompatibleVersion(read_version)) };
122
123 Ok(Self {
125 path: path.to_path_buf(),
126 compressed: false,
127 })
128 }
129
130 pub fn load_db(path: impl AsRef<Path>) -> Result<Self, LDBError> {
138 let path = path.as_ref();
139 let mod_path = path.with_extension("modb");
140
141 if mod_path.is_dir() { return Self::load_dir(mod_path) }
143
144 Self::decompile(path, &mod_path)?;
146 let mut ldb = Self::load_dir(mod_path)?;
147 ldb.compressed = true;
148
149 Ok(ldb)
150 }
151
152 #[inline]
154 pub fn as_container(&self) -> Result<LazyContainer, LDBError> {
155 LazyContainer::load(&self.path)
156 }
157
158 #[inline]
159 pub fn path(&self) -> &Path {
160 &self.path
161 }
162
163 pub fn compile(&self, out_path: impl AsRef<Path>) -> Result<(), std::io::Error> {
165 use lazy_archive::*; let tar = self.path.with_extension("tmp.tar");
167
168 build_tar(&self.path, &tar)?; compress_file(&tar, &out_path)?;
171
172 fs::remove_file(tar)?;
174
175 Ok(())
176 }
177
178 pub fn decompile(path: impl AsRef<Path>, out_path: impl AsRef<Path>) -> Result<(), LDBError> {
180 use lazy_archive::*; let path = path.as_ref();
182
183 if !path.is_file() { return Err(LDBError::FileNotFound(path.to_path_buf())) };
185
186 let tar = path.with_extension("tmp.tar");
188 unwrap_result!((decompress_file(path, &tar)) err => LDBError::IOError(err));
189 unwrap_result!((unpack_tar(&tar, out_path)) err => LDBError::IOError(err));
190
191 unwrap_result!((fs::remove_file(tar)) err => LDBError::IOError(err));
193
194 Ok(())
195 }
196}
197
198impl Drop for LazyDB {
199 fn drop(&mut self) {
200 if !self.compressed { return }; let ok = self.compile(self.path.with_extension("ldb")).is_ok();
202 if !ok { return }; let _ = fs::remove_dir_all(&self.path);
204 }
205}