1use crate::error;
14
15pub trait Backend {
23 fn get_data(&mut self) -> error::BackendResult<Vec<u8>>;
25
26 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()>;
28}
29
30impl Backend for Box<dyn Backend> {
31 fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
32 use std::ops::DerefMut;
33 self.deref_mut().get_data()
34 }
35
36 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
37 use std::ops::DerefMut;
38 self.deref_mut().put_data(data)
39 }
40}
41
42impl<T: Backend> Backend for Box<T> {
43 fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
44 use std::ops::DerefMut;
45 self.deref_mut().get_data()
46 }
47
48 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
49 use std::ops::DerefMut;
50 self.deref_mut().put_data(data)
51 }
52}
53
54#[cfg(feature = "mmap")]
55mod mmap;
56#[cfg(feature = "mmap")]
57pub use mmap::MmapStorage;
58
59mod path;
60pub use path::PathBackend;
61
62#[derive(Debug)]
64pub struct FileBackend(std::fs::File);
65
66impl Backend for FileBackend {
67 fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
68 use std::io::{Read, Seek, SeekFrom};
69
70 let mut buffer = vec![];
71 self.0.seek(SeekFrom::Start(0))?;
72 self.0.read_to_end(&mut buffer)?;
73 Ok(buffer)
74 }
75
76 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
77 use std::io::{Seek, SeekFrom, Write};
78
79 self.0.seek(SeekFrom::Start(0))?;
80 self.0.set_len(0)?;
81 self.0.write_all(data)?;
82 self.0.sync_all()?;
83 Ok(())
84 }
85}
86
87impl FileBackend {
88 #[must_use]
90 pub fn from_file(file: std::fs::File) -> Self {
91 Self(file)
92 }
93
94 #[must_use]
96 pub fn into_inner(self) -> std::fs::File {
97 self.0
98 }
99}
100
101impl FileBackend {
102 pub fn from_path_or_fail<P: AsRef<std::path::Path>>(path: P) -> error::BackendResult<Self> {
105 use std::fs::OpenOptions;
106
107 Ok(Self(OpenOptions::new().read(true).write(true).open(path)?))
108 }
109
110 pub fn from_path_or_create<P: AsRef<std::path::Path>>(
115 path: P,
116 ) -> error::BackendResult<(Self, bool)> {
117 use std::fs::OpenOptions;
118
119 let exists = path.as_ref().is_file();
120 Ok((
121 Self(
122 OpenOptions::new()
123 .read(true)
124 .write(true)
125 .create(true)
126 .open(path)?,
127 ),
128 exists,
129 ))
130 }
131
132 pub fn from_path_or_create_and<P, C>(path: P, closure: C) -> error::BackendResult<Self>
135 where
136 C: FnOnce(&mut std::fs::File),
137 P: AsRef<std::path::Path>,
138 {
139 Self::from_path_or_create(path).map(|(mut b, exists)| {
140 if !exists {
141 closure(&mut b.0)
142 }
143 b
144 })
145 }
146}
147
148#[derive(Debug, Default)]
152pub struct MemoryBackend(Vec<u8>);
153
154impl MemoryBackend {
155 #[must_use]
157 pub fn new() -> Self {
158 Self::default()
159 }
160}
161
162impl Backend for MemoryBackend {
163 fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
164 println!("Returning data: {:?}", &self.0);
165 Ok(self.0.clone())
166 }
167
168 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
169 println!("Writing data: {:?}", data);
170 self.0 = data.to_owned();
171 Ok(())
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::{Backend, FileBackend, MemoryBackend};
178 use std::io::{Read, Seek, SeekFrom, Write};
179 use tempfile::NamedTempFile;
180
181 #[test]
182 fn test_memory_backend() {
183 let mut backend = MemoryBackend::new();
184 let data = [4, 5, 1, 6, 8, 1];
185
186 backend.put_data(&data).expect("could not put data");
187 assert_eq!(backend.get_data().expect("could not get data"), data);
188 }
189
190 #[test]
191 #[cfg_attr(miri, ignore)]
192 fn test_file_backend_from_file() {
193 let file = tempfile::tempfile().expect("could not create temporary file");
194 let mut backend = FileBackend::from_file(file);
195 let data = [4, 5, 1, 6, 8, 1];
196 let data2 = [3, 99, 127, 6];
197
198 backend.put_data(&data).expect("could not put data");
199 assert_eq!(backend.get_data().expect("could not get data"), data);
200
201 backend.put_data(&data2).expect("could not put data");
202 assert_eq!(backend.get_data().expect("could not get data"), data2);
203 }
204
205 #[test]
206 #[cfg_attr(miri, ignore)]
207 fn test_file_backend_from_path_existing() {
208 let file = NamedTempFile::new().expect("could not create temporary file");
209 let (mut backend, existed) =
210 FileBackend::from_path_or_create(file.path()).expect("could not create backend");
211 assert!(existed);
212 let data = [4, 5, 1, 6, 8, 1];
213
214 backend.put_data(&data).expect("could not put data");
215 assert_eq!(backend.get_data().expect("could not get data"), data);
216 }
217
218 #[test]
219 #[cfg_attr(miri, ignore)]
220 fn test_file_backend_from_path_new() {
221 let dir = tempfile::tempdir().expect("could not create temporary directory");
222 let mut file_path = dir.path().to_owned();
223 file_path.push("rustbreak_path_db.db");
224 let (mut backend, existed) =
225 FileBackend::from_path_or_create(file_path).expect("could not create backend");
226 assert!(!existed);
227 let data = [4, 5, 1, 6, 8, 1];
228
229 backend.put_data(&data).expect("could not put data");
230 assert_eq!(backend.get_data().expect("could not get data"), data);
231 dir.close().expect("Error while deleting temp directory!");
232 }
233
234 #[test]
235 #[cfg_attr(miri, ignore)]
236 fn test_file_backend_from_path_nofail() {
237 let file = NamedTempFile::new().expect("could not create temporary file");
238 let file_path = file.path().to_owned();
239 let mut backend = FileBackend::from_path_or_fail(file_path).expect("should not fail");
240 let data = [4, 5, 1, 6, 8, 1];
241
242 backend.put_data(&data).expect("could not put data");
243 assert_eq!(backend.get_data().expect("could not get data"), data);
244 }
245
246 #[test]
247 #[cfg_attr(miri, ignore)]
248 fn test_file_backend_from_path_fail_notfound() {
249 let dir = tempfile::tempdir().expect("could not create temporary directory");
250 let mut file_path = dir.path().to_owned();
251 file_path.push("rustbreak_path_db.db");
252 let err =
253 FileBackend::from_path_or_fail(file_path).expect_err("should fail with file not found");
254 if let crate::error::BackendError::Io(io_err) = &err {
255 assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
256 } else {
257 panic!("Wrong kind of error returned: {}", err);
258 };
259 dir.close().expect("Error while deleting temp directory!");
260 }
261
262 #[test]
263 #[cfg_attr(miri, ignore)]
264 fn test_file_backend_into_inner() {
265 let file = tempfile::tempfile().expect("could not create temporary file");
266 let mut backend = FileBackend::from_file(file);
267 let data = [4, 5, 1, 6, 8, 1];
268
269 backend.put_data(&data).expect("could not put data");
270 assert_eq!(backend.get_data().expect("could not get data"), data);
271
272 let mut file = backend.into_inner();
273 file.seek(SeekFrom::Start(0)).unwrap();
274 let mut contents = Vec::new();
275 assert_eq!(file.read_to_end(&mut contents).unwrap(), 6);
276 assert_eq!(&contents[..], &data[..]);
277 }
278
279 #[test]
280 fn allow_boxed_backends() {
281 let mut backend = Box::new(MemoryBackend::new());
282 let data = [4, 5, 1, 6, 8, 1];
283
284 backend.put_data(&data).unwrap();
285 assert_eq!(backend.get_data().unwrap(), data);
286 }
287
288 #[test]
290 #[cfg_attr(miri, ignore)]
291 fn test_file_backend_create_and_existing_nocall() {
292 let file = NamedTempFile::new().expect("could not create temporary file");
293 let mut backend = FileBackend::from_path_or_create_and(file.path(), |_| {
294 panic!("Closure called but file already existed");
295 })
296 .expect("could not create backend");
297 let data = [4, 5, 1, 6, 8, 1];
298
299 backend.put_data(&data).expect("could not put data");
300 assert_eq!(backend.get_data().expect("could not get data"), data);
301 }
302
303 #[test]
305 #[cfg_attr(miri, ignore)]
306 fn test_file_backend_create_and_new() {
307 let dir = tempfile::tempdir().expect("could not create temporary directory");
308 let mut file_path = dir.path().to_owned();
309 file_path.push("rustbreak_path_db.db");
310 let mut backend = FileBackend::from_path_or_create_and(file_path, |f| {
311 f.write_all(b"this is a new file")
312 .expect("could not write to file")
313 })
314 .expect("could not create backend");
315 assert_eq!(
316 backend.get_data().expect("could not get data"),
317 b"this is a new file"
318 );
319 let data = [4, 5, 1, 6, 8, 1];
320
321 backend.put_data(&data).expect("could not put data");
322 assert_eq!(backend.get_data().expect("could not get data"), data);
323 dir.close().expect("Error while deleting temp directory!");
324 }
325}