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