1
2use std::path::PathBuf;
3use std::{io, fs};
4use std::ops::{Deref, DerefMut};
5
6use serde::{de::DeserializeOwned, Serialize};
7
8#[derive(Debug)]
9pub struct FileDb<T> {
10 path: PathBuf,
11 tmp_path: PathBuf,
12 data: T
13}
14
15impl<T> FileDb<T> {
16
17 fn sanitze_paths(mut main: PathBuf) -> (PathBuf, PathBuf) {
18 if main.file_name().is_none() {
19 main.set_file_name("");
20 main.set_extension("fdb");
21 }
22 let mut tmp = main.clone().into_os_string();
23 tmp.push(".tmp");
24 (main, tmp.into())
25 }
26
27 pub fn new(path: impl Into<PathBuf>, data: T) -> Self {
29 let (path, tmp_path) = Self::sanitze_paths(path.into());
30
31 Self { path, tmp_path, data }
32 }
33
34 pub fn open_sync(path: impl Into<PathBuf>) -> io::Result<Self>
36 where T: DeserializeOwned {
37 let (path, tmp_path) = Self::sanitze_paths(path.into());
38
39 let v = fs::read(&path)?;
41 let data = serde_json::from_slice(&v)
42 .map_err(json_err)?;
43
44 Ok(Self { path, tmp_path, data })
45 }
46
47 pub fn read_sync(&mut self) -> io::Result<()>
49 where T: DeserializeOwned {
50 let v = fs::read(&self.path)?;
51 let data = serde_json::from_slice(&v)
52 .map_err(json_err)?;
53
54 self.data = data;
55 Ok(())
56 }
57
58 pub fn write_sync(&self) -> io::Result<()>
59 where T: Serialize {
60 let v = serde_json::to_vec(&self.data)
61 .map_err(json_err)?;
62
63 fs::write(&self.tmp_path, v)?;
65 fs::rename(&self.tmp_path, &self.path)
67 }
68
69 pub fn data(&self) -> &T {
70 &self.data
71 }
72
73 pub fn data_mut(&mut self) -> &mut T {
74 &mut self.data
75 }
76
77 pub fn into_data(self) -> T {
78 self.data
79 }
80
81}
82
83impl<T> Deref for FileDb<T> {
84 type Target = T;
85 fn deref(&self) -> &T {
86 self.data()
87 }
88}
89
90impl<T> DerefMut for FileDb<T> {
91 fn deref_mut(&mut self) -> &mut T {
92 self.data_mut()
93 }
94}
95
96#[cfg(any(feature = "async", test))]
97impl<T> FileDb<T> {
98
99 pub async fn open(path: impl Into<PathBuf>) -> io::Result<Self>
101 where T: DeserializeOwned {
102 let (path, tmp_path) = Self::sanitze_paths(path.into());
103
104 let v = tokio::fs::read(&path).await?;
106 let data = serde_json::from_slice(&v)
107 .map_err(json_err)?;
108
109 Ok(Self { path, tmp_path, data })
110 }
111
112 pub async fn read(&mut self) -> io::Result<()>
114 where T: DeserializeOwned {
115 let v = tokio::fs::read(&self.path).await?;
116 let data = serde_json::from_slice(&v)
117 .map_err(json_err)?;
118
119 self.data = data;
120 Ok(())
121 }
122
123 pub async fn write(&self) -> io::Result<()>
124 where T: Serialize {
125 let v = serde_json::to_vec(&self.data)
126 .map_err(json_err)?;
127
128 tokio::fs::write(&self.tmp_path, v).await?;
130 tokio::fs::rename(&self.tmp_path, &self.path).await
132 }
133
134}
135
136fn json_err(e: serde_json::Error) -> io::Error {
137 io::Error::new(io::ErrorKind::Other, e)
138}
139
140
141#[cfg(test)]
142mod tests {
143
144 use super::*;
145 use serde::Deserialize;
146 use std::path::PathBuf;
147 use std::fs;
148
149 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
150 pub struct Data {
151 inner: Vec<u32>
152 }
153
154 const TEST_TMP: &str = "./tests_tmp";
155
156 fn test_tmp() -> PathBuf {
157 let p = PathBuf::from(TEST_TMP);
158 if !p.is_dir() {
159 let _ = fs::create_dir(&p);
160 }
161 p
162 }
163
164 #[test]
165 fn test_sync() {
166 let mut file = test_tmp();
167 file.push("test_sync.fdb");
168
169 let data = Data {
170 inner: vec![1, 4, 8, 14]
171 };
172
173 let mut db = FileDb::new(file.clone(), data.clone());
174 db.write_sync().unwrap();
175
176 let mut db_2: FileDb<Data> = FileDb::open_sync(file.clone()).unwrap();
178 assert_eq!(db_2.data(), &data);
179
180 db.data_mut().inner.push(420);
182 db.write_sync().unwrap();
183
184 db_2.read_sync().unwrap();
186 assert_ne!(db_2.data(), &data);
187 assert_eq!(db_2.data().inner.last().unwrap(), &420);
188
189 }
190
191 #[tokio::test]
192 async fn test_async() {
193 let mut file = test_tmp();
194 file.push("test_async.fdb");
195
196 let data = Data {
197 inner: vec![1, 4, 8, 14]
198 };
199
200 let mut db = FileDb::new(file.clone(), data.clone());
201 db.write().await.unwrap();
202
203 let mut db_2: FileDb<Data> = FileDb::open(file.clone()).await.unwrap();
205 assert_eq!(db_2.data(), &data);
206
207 db.data_mut().inner.push(420);
209 db.write().await.unwrap();
210
211 db_2.read().await.unwrap();
213 assert_ne!(db_2.data(), &data);
214 assert_eq!(db_2.data().inner.last().unwrap(), &420);
215
216 }
217
218}