lazy_db/
lazy_container.rs

1use crate::*;
2use std::path::{Path, PathBuf};
3use std::fs;
4
5/// Used for reading from a `LazyContainer` with less boiler-plate
6#[macro_export]
7macro_rules! write_container {
8    (($container:expr) $($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
9        let container = &$container;
10        $(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
11        $(LazyData::$func(container.data_writer($obj)?, $value)?;)?
12        Result::<(), LDBError>::Ok(())
13    })()};
14
15    (($container:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))? = $func:ident($value:expr)) => {(|| {
16        let mut container = &mut $container;
17        $({
18            let con = $(stringify!($con))?$($can)?;
19            container = match container.read_container(con) {
20                Ok(x) => x,
21                Err(LDBError::DirNotFound(_)) => container.new_container(con)?,
22                Err(e) => return Err(e),
23            }
24        };)*
25
26        $(LazyData::$func(container.data_writer(stringify!($item))?, $value)?;)?
27        $(LazyData::$func(container.data_writer($obj)?, $value)?;)?
28        Result::<(), LDBError>::Ok(())
29    })()}
30}
31
32/// Used for reading from a `LazyContainer` with less boiler-plate
33#[macro_export]
34macro_rules! search_container {
35    (($container:expr) /$($($con:ident)?$(($can:expr))?)/ *) => {(|| {
36        let container = &$container;
37        $(
38            $(let container = container.read_container(stringify!($con))?;)?
39            $(let container = container.read_container($can)?;)?
40        )*
41        let result: Result<LazyContainer, LDBError> = Ok(container);
42        result
43    })()};
44
45    (($container:expr) /$($($con:ident)?$(($can:expr))?)/ *::$($item:ident)?$(($obj:expr))?) => {(|| {
46        let container = search_container!(($container) /$($($con)?$(($can))?)/ *)?;
47        $(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
48        $(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
49        result
50    })()};
51
52    (($container:expr) $($item:ident)?$(($obj:expr))?) => {(|| {
53        let container = &$container;
54        $(let result: Result<LazyData, LDBError> = container.read_data(stringify!($item));)?
55        $(let result: Result<LazyData, LDBError> = container.read_data($obj);)?
56        result
57    })()};
58}
59
60/// A wrapper for a directory that holds individual `LazyData` files
61pub struct LazyContainer {
62    path: PathBuf,
63}
64
65impl LazyContainer {
66    /// Initialises a new, empty `LazyContainer` at the specified path.
67    pub fn init(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
68        let path = path.as_ref();
69
70        // Checks if path exists or not
71        if !path.is_dir() { fs::create_dir_all(path)? };
72        
73        // Constructs self
74        Ok(Self {
75            path: path.to_path_buf(),
76        })
77    }
78
79    /// Loads a pre-existing `LazyContainer` directory at a specified path.
80    /// 
81    /// Will throw an error if the directory doesn't exist or there is an `io::Error`.
82    pub fn load(path: impl AsRef<Path>) -> Result<Self, LDBError> {
83        let path = path.as_ref().to_path_buf();
84
85        // Checks if path exists or not
86        if !path.is_dir() { return Err(LDBError::DirNotFound(path)) };
87
88        // Constructs self
89        Ok(Self {
90            path,
91        })
92    }
93
94    /// Generates a `FileWrapper` in write mode from a key (like a relative file path)
95    /// 
96    /// If the data already exists, it will try to remove it
97    pub fn data_writer(&self, key: impl AsRef<Path>) -> Result<FileWrapper, LDBError> {
98        let path = self.path.join(key);
99        if path.is_file() { let _ = fs::remove_file(&path); }; // if files exists try remove it
100        let file = unwrap_result!((fs::File::create(path)) err => LDBError::IOError(err));
101        Ok(FileWrapper::new_writer(file))
102    }
103
104    /// Generates a nested `LazyContainer` within this container
105    /// 
106    /// If container already exists it will **wipe** and **replace** it.
107    pub fn new_container(&self, key: impl AsRef<Path>) -> Result<LazyContainer, LDBError> {
108        let path = self.path.join(&key);
109        if path.is_dir() { unwrap_result!((fs::remove_dir_all(&path)) err => LDBError::IOError(err)) }; // If exists wipe it
110        Ok(unwrap_result!((LazyContainer::init(path)) err => LDBError::IOError(err)))
111    }
112
113    /// Gets a nested `LazyContainer` within this container
114    /// 
115    /// If container already exists it will load it
116    /// Otherwise it will initialise a new one
117    pub fn child_container(&self, key: impl AsRef<Path>) -> Result<LazyContainer, LDBError> {
118        let path = self.path.join(&key);
119        if path.is_dir() { return self.read_container(key) }; // If exists load instead
120        Ok(unwrap_result!((LazyContainer::init(path)) err => LDBError::IOError(err)))
121    }
122
123    /// Reads nested `LazyData` within this container
124    pub fn read_data(&self, key: impl AsRef<Path>) -> Result<LazyData, LDBError> {
125        let path = self.path.join(key);
126        if !path.is_file() { return Err(LDBError::FileNotFound(path)) };
127        LazyData::load(path)
128    }
129
130    /// Reads nexted `LazyContainer` within this container
131    pub fn read_container(&self, key: impl AsRef<Path>) -> Result<LazyContainer, LDBError> {
132        let path = self.path.join(key);
133        if !path.is_dir() { return Err(LDBError::DirNotFound(path)) };
134        LazyContainer::load(path)
135    }
136
137    /// Tries to remove item at specified key; returns result
138    pub fn remove(&self, key: impl AsRef<Path>) -> Result<(), std::io::Error> {
139        let path = self.path.join(key);
140        if path.is_dir() {
141            fs::remove_dir_all(path)
142        } else {
143            fs::remove_file(path)
144        }
145    }
146
147    /// Tries to wipe container's contents; returns result
148    pub fn wipe(&self) -> Result<(), std::io::Error> {
149        fs::remove_dir_all(&self.path)?;
150        fs::create_dir_all(&self.path)
151    }
152
153    /// Returns a reference to the container's path
154    #[inline]
155    pub fn path(&self) -> &Path {
156        &self.path
157    }
158}