docdb/
db.rs

1use serde::de::DeserializeOwned;
2use serde::Serialize;
3
4use crate::error::{DocError, Result};
5use crate::iterator::DocDbIterator;
6use crate::serialization::{SerializationMethod, Serializer};
7use std::fs;
8use std::time::{SystemTime, UNIX_EPOCH};
9use std::{
10    collections::HashMap,
11    path::{Path, PathBuf},
12    time::{Duration, Instant},
13};
14
15/// An enum that determines the policy of dumping DocDb changes into the file
16pub enum DumpPolicy {
17    /// Never dump any change, file will always remain read-only
18    NeverDump,
19    AutoDump,
20    DumpRelyRequest,
21    PeriodicDump(Duration),
22}
23
24pub struct DocDb {
25    ///
26    map: HashMap<String, Vec<u8>>,
27    serializer: Serializer,
28    db_file_path: PathBuf,
29    dump_policy: DumpPolicy,
30    last_dump: Instant,
31}
32
33impl DocDb {
34    pub fn new<P: AsRef<Path>>(
35        db_path: P,
36        dump_policy: DumpPolicy,
37        serialize_method: SerializationMethod,
38    ) -> Self {
39        let mut path_buf = PathBuf::new();
40        path_buf.push(db_path);
41
42        Self {
43            map: HashMap::new(),
44            serializer: Serializer::new(serialize_method),
45            db_file_path: path_buf,
46            dump_policy: dump_policy,
47            last_dump: Instant::now(),
48        }
49    }
50
51    #[cfg(feature = "json")]
52    pub fn new_json<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Self {
53        DocDb::new(db_path, dump_policy, SerializationMethod::Json)
54    }
55
56    #[cfg(feature = "yaml")]
57    pub fn new_yaml<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Self {
58        DocDb::new(db_path, dump_policy, SerializationMethod::Yaml)
59    }
60
61    #[cfg(feature = "bincode")]
62    pub fn new_bincode<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Self {
63        DocDb::new(db_path, dump_policy, SerializationMethod::Bin)
64    }
65
66    pub fn load<P: AsRef<Path>>(
67        db_path: P,
68        dump_policy: DumpPolicy,
69        ser_method: SerializationMethod,
70    ) -> Result<DocDb> {
71        let content = match fs::read(db_path.as_ref()) {
72            Ok(file_content) => file_content,
73            Err(err) => return Err(DocError::IO(err)),
74        };
75
76        let serializer = Serializer::new(ser_method);
77
78        let maps_from_file = match serializer.deserialize_db(&content) {
79            Ok(maps) => maps,
80            Err(err) => return Err(err),
81        };
82
83        let mut db_path_buf = PathBuf::new();
84        db_path_buf.push(db_path);
85
86        Ok(DocDb {
87            map: maps_from_file,
88            serializer,
89            db_file_path: db_path_buf,
90            dump_policy,
91            last_dump: Instant::now(),
92        })
93    }
94
95    pub fn load_read_only<P: AsRef<Path>>(
96        db_path: P,
97        serialization_method: SerializationMethod,
98    ) -> Result<DocDb> {
99        DocDb::load(db_path, DumpPolicy::NeverDump, serialization_method)
100    }
101
102    #[cfg(feature = "json")]
103    pub fn load_json<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Result<Self> {
104        DocDb::load(db_path, dump_policy, SerializationMethod::Json)
105    }
106
107    #[cfg(feature = "yaml")]
108    pub fn load_yaml<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Result<Self> {
109        Self::load(db_path, dump_policy, SerializationMethod::Yaml)
110    }
111
112    #[cfg(feature = "bincode")]
113    pub fn load_bin<P: AsRef<Path>>(db_path: P, dump_policy: DumpPolicy) -> Result<Self> {
114        Self::load(db_path, dump_policy, SerializationMethod::Bin)
115    }
116
117    pub fn dump(&mut self) -> Result<()> {
118        if let DumpPolicy::NeverDump = self.dump_policy {
119            return Ok(());
120        }
121
122        match self.serializer.serialize_db(&self.map) {
123            Ok(ser_data) => {
124                let temp_file_path = format!(
125                    "{}.temp.{}",
126                    self.db_file_path.to_str().unwrap(),
127                    SystemTime::now()
128                        .duration_since(UNIX_EPOCH)
129                        .unwrap()
130                        .as_secs()
131                );
132
133                fs::write(&temp_file_path, ser_data)?;
134                // match fs::write(&temp_file_path, ser_data) {
135                //     Ok(_) => (),
136                //     Err(err) => return Err(DocError::IO(err)),
137                // }
138
139                fs::rename(temp_file_path, &self.db_file_path)?;
140                // match fs::rename(temp_file_path, &self.db_file_path) {
141                //     Ok(_) => (),
142                //     Err(err) => return Err(DocError::IO(err)),
143                // }
144
145                if let DumpPolicy::PeriodicDump(_dur) = self.dump_policy {
146                    self.last_dump = Instant::now();
147                }
148                Ok(())
149            }
150            Err(err) => Err(err),
151        }
152    }
153
154    pub fn dump_now(&mut self) -> Result<()> {
155        match self.dump_policy {
156            DumpPolicy::AutoDump => self.dump(),
157            DumpPolicy::PeriodicDump(duration) => {
158                //
159                if Instant::now().duration_since(self.last_dump) > duration {
160                    self.last_dump = Instant::now();
161                    self.dump()?;
162                }
163                Ok(())
164            }
165            _ => Ok(()),
166        }
167    }
168
169    pub fn set<T: Serialize>(&mut self, key: &str, val: &T) -> Result<()> {
170        let ser_data = match self.serializer.serialize_data(val) {
171            Ok(data) => data,
172            Err(err) => return Err(err),
173        };
174
175        let original_val = self.map.insert(key.to_string(), ser_data);
176
177        match self.dump_now() {
178            Ok(_) => Ok(()),
179            // set value failed, need to roll back
180            Err(err) => {
181                match original_val {
182                    // not exist before insert
183                    None => self.map.remove(key),
184                    // exist and reset old val
185                    Some(orig_val) => self.map.insert(String::from(key), orig_val.to_vec()),
186                };
187
188                Err(err)
189            }
190        }
191    }
192
193    pub fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
194        match self.map.get(key) {
195            Some(v) => self.serializer.deserialize_data(v),
196            None => None,
197        }
198    }
199
200    pub fn exist(&self, key: &str) -> bool {
201        self.map.get(key).is_some()
202    }
203
204    /// Get a vector of all the keys in the DB.
205    ///
206    /// The keys returned in the vector are not references to the actual key string
207    /// objects but rather a clone of them.
208    pub fn get_all_keys(&self) -> Vec<String> {
209        self.map.keys().cloned().collect()
210    }
211
212    /// Get the total number of keys in the DB.
213    pub fn total_nums(&self) -> usize {
214        self.map.len()
215    }
216
217    pub fn rem(&mut self, key: &str) -> Result<bool> {
218        let remove_map = match self.map.remove(key) {
219            // exists key, return old value and dump db now
220            Some(v) => match self.dump_now() {
221                // dump successfully, return some(v)
222                Ok(_) => Some(v),
223                // dump failed, restore key in map
224                Err(err) => {
225                    self.map.insert(key.to_string(), v);
226                    return Err(err);
227                }
228            },
229            None => None,
230        };
231
232        Ok(remove_map.is_some())
233    }
234
235    pub fn iter(&self) -> DocDbIterator {
236        DocDbIterator {
237            map_iter: self.map.iter(),
238            serializer: &self.serializer,
239        }
240    }
241}
242
243impl Drop for DocDb {
244    fn drop(&mut self) {
245        if !matches!(
246            self.dump_policy,
247            DumpPolicy::NeverDump | DumpPolicy::DumpRelyRequest
248        ) {
249            let _ = self.dump();
250        }
251    }
252}