1use super::Backend;
9use crate::error;
10use std::fs::OpenOptions;
11use std::path::{Path, PathBuf};
12use tempfile::NamedTempFile;
13
14#[derive(Debug)]
19pub struct PathBackend {
20 path: PathBuf,
21}
22
23impl PathBackend {
24 pub fn from_path_or_fail(path: PathBuf) -> error::BackendResult<Self> {
27 OpenOptions::new().read(true).open(path.as_path())?;
28 Ok(Self { path })
29 }
30
31 pub fn from_path_or_create(path: PathBuf) -> error::BackendResult<(Self, bool)> {
36 let exists = path.as_path().is_file();
37 OpenOptions::new()
38 .write(true)
39 .create(true)
40 .open(path.as_path())?;
41 Ok((Self { path }, exists))
42 }
43
44 pub fn from_path_or_create_and<C>(path: PathBuf, closure: C) -> error::BackendResult<Self>
47 where
48 C: FnOnce(&mut std::fs::File),
49 {
50 let exists = path.as_path().is_file();
51 let mut file = OpenOptions::new()
52 .read(true)
53 .write(true)
54 .create(true)
55 .open(path.as_path())?;
56 if !exists {
57 closure(&mut file)
58 }
59 Ok(Self { path })
60 }
61}
62
63impl Backend for PathBackend {
64 fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
65 use std::io::Read;
66
67 let mut file = OpenOptions::new().read(true).open(self.path.as_path())?;
68 let mut buffer = vec![];
69 file.read_to_end(&mut buffer)?;
70 Ok(buffer)
71 }
72
73 fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
78 use std::io::Write;
79
80 #[allow(clippy::or_fun_call)] let mut tempf = NamedTempFile::new_in(self.path.parent().unwrap_or(Path::new(".")))?;
82 tempf.write_all(data)?;
83 tempf.as_file().sync_all()?;
84 tempf.persist(self.path.as_path())?;
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::{Backend, PathBackend};
92 use std::io::Write;
93 use tempfile::NamedTempFile;
94
95 #[test]
96 #[cfg_attr(miri, ignore)]
97 fn test_path_backend_existing() {
98 let file = NamedTempFile::new().expect("could not create temporary file");
99 let (mut backend, existed) = PathBackend::from_path_or_create(file.path().to_owned())
100 .expect("could not create backend");
101 assert!(existed);
102 let data = [4, 5, 1, 6, 8, 1];
103
104 backend.put_data(&data).expect("could not put data");
105 assert_eq!(backend.get_data().expect("could not get data"), data);
106 }
107
108 #[test]
109 #[cfg_attr(miri, ignore)]
110 fn test_path_backend_new() {
111 let dir = tempfile::tempdir().expect("could not create temporary directory");
112 let mut file_path = dir.path().to_owned();
113 file_path.push("rustbreak_path_db.db");
114 let (mut backend, existed) =
115 PathBackend::from_path_or_create(file_path).expect("could not create backend");
116 assert!(!existed);
117 let data = [4, 5, 1, 6, 8, 1];
118
119 backend.put_data(&data).expect("could not put data");
120 assert_eq!(backend.get_data().expect("could not get data"), data);
121 dir.close().expect("Error while deleting temp directory!");
122 }
123
124 #[test]
125 #[cfg_attr(miri, ignore)]
126 fn test_path_backend_nofail() {
127 let file = NamedTempFile::new().expect("could not create temporary file");
128 let file_path = file.path().to_owned();
129 let mut backend = PathBackend::from_path_or_fail(file_path).expect("should not fail");
130 let data = [4, 5, 1, 6, 8, 1];
131
132 backend.put_data(&data).expect("could not put data");
133 assert_eq!(backend.get_data().expect("could not get data"), data);
134 }
135
136 #[test]
137 #[cfg_attr(miri, ignore)]
138 fn test_path_backend_fail_notfound() {
139 let dir = tempfile::tempdir().expect("could not create temporary directory");
140 let mut file_path = dir.path().to_owned();
141 file_path.push("rustbreak_path_db.db");
142 let err =
143 PathBackend::from_path_or_fail(file_path).expect_err("should fail with file not found");
144 if let crate::error::BackendError::Io(io_err) = &err {
145 assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
146 } else {
147 panic!("Wrong kind of error returned: {}", err);
148 };
149 dir.close().expect("Error while deleting temp directory!");
150 }
151
152 #[test]
154 #[cfg_attr(miri, ignore)]
155 fn test_path_backend_create_and_existing_nocall() {
156 let file = NamedTempFile::new().expect("could not create temporary file");
157 let mut backend = PathBackend::from_path_or_create_and(file.path().to_owned(), |_| {
158 panic!("Closure called but file already existed");
159 })
160 .expect("could not create backend");
161 let data = [4, 5, 1, 6, 8, 1];
162
163 backend.put_data(&data).expect("could not put data");
164 assert_eq!(backend.get_data().expect("could not get data"), data);
165 }
166
167 #[test]
169 #[cfg_attr(miri, ignore)]
170 fn test_path_backend_create_and_new() {
171 let dir = tempfile::tempdir().expect("could not create temporary directory");
172 let mut file_path = dir.path().to_owned();
173 file_path.push("rustbreak_path_db.db");
174 let mut backend = PathBackend::from_path_or_create_and(file_path, |f| {
175 f.write_all(b"this is a new file")
176 .expect("could not write to file")
177 })
178 .expect("could not create backend");
179 assert_eq!(
180 backend.get_data().expect("could not get data"),
181 b"this is a new file"
182 );
183 let data = [4, 5, 1, 6, 8, 1];
184
185 backend.put_data(&data).expect("could not put data");
186 assert_eq!(backend.get_data().expect("could not get data"), data);
187 dir.close().expect("Error while deleting temp directory!");
188 }
189}