dscfg_cached_file_storage/
lib.rs1extern crate dscfg_server;
2extern crate serde_json;
3extern crate void;
4
5use dscfg_server::{IsFatalError, Storage};
6use std::path::{Path, PathBuf};
7use std::io;
8use std::fs::File;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Eq, PartialEq, Hash)]
12enum IoOperation {
13 Open(PathBuf),
14 Write(PathBuf),
15 Move(PathBuf, PathBuf),
16}
17
18#[derive(Debug)]
19pub struct StorageError {
20 operation: IoOperation,
21 error: io::Error,
22}
23
24impl StorageError {
25 fn open_error(file: impl Into<PathBuf>, error: io::Error) -> Self {
26 StorageError {
27 operation: IoOperation::Open(file.into()),
28 error,
29 }
30 }
31
32 fn write_error(file: impl Into<PathBuf>, error: impl Into<io::Error>) -> Self {
33 StorageError {
34 operation: IoOperation::Write(file.into()),
35 error: error.into(),
36 }
37 }
38
39 fn move_error(from: impl Into<PathBuf>, to: impl Into<PathBuf>, error: io::Error) -> Self {
40 StorageError {
41 operation: IoOperation::Move(from.into(), to.into()),
42 error,
43 }
44 }
45}
46
47impl IsFatalError for StorageError {
48 fn is_fatal(&self) -> bool {
49 if let IoOperation::Write(_) = self.operation {
50 true
51 } else {
52 self.error.kind() != io::ErrorKind::Interrupted &&
53 self.error.kind() != io::ErrorKind::WouldBlock
54 }
55 }
56}
57
58pub struct CachedFileStorage {
59 file_path: PathBuf,
60 temp_file: PathBuf,
61 data: HashMap<String, serde_json::Value>,
62}
63
64impl CachedFileStorage {
65 pub fn load_or_create<P: AsRef<Path> + Into<PathBuf>>(file: P) -> io::Result<Self> {
66 let data = match File::open(file.as_ref()) {
67 Ok(file) => serde_json::from_reader(file)?,
68 Err(ref err) if err.kind() == io::ErrorKind::NotFound => Default::default(),
69 Err(err) => return Err(err),
70 };
71
72 let temp_file = Self::temp_file_path(file.as_ref())?;
73 let file_path = file.into();
74
75 Ok(CachedFileStorage {
76 file_path,
77 temp_file,
78 data,
79 })
80 }
81
82 fn temp_file_path(original_path: &Path) -> io::Result<PathBuf> {
83 use std::ffi::OsString;
84
85 let file_name = original_path.file_name().ok_or(io::ErrorKind::InvalidInput)?;
86 let mut temp_file_name: OsString = ".".into();
87 temp_file_name.push(file_name);
88 temp_file_name.push(".tmp");
89 let mut temp_file = original_path.parent().map(PathBuf::from).unwrap_or_else(PathBuf::new);
90 temp_file.push(temp_file_name);
91
92 Ok(temp_file)
93 }
94}
95
96impl Storage for CachedFileStorage {
97 type SetError = StorageError;
98 type GetError = void::Void;
99
100 fn set(&mut self, key: String, value: serde_json::Value) -> Result<(), Self::SetError> {
101 self.data.insert(key, value);
103 {
105 let mut file = File::create(&self.temp_file).map_err(|err| StorageError::open_error(&self.temp_file, err))?;
106 serde_json::to_writer(&mut file, &self.data).map_err(|err| StorageError::write_error(&self.temp_file, err))?;
107 file.sync_data().map_err(|err| StorageError::write_error(&self.temp_file, err))?;
108 }
109 std::fs::rename(&self.temp_file, &self.file_path).map_err(|err| StorageError::move_error(&self.temp_file, &self.file_path, err))?;
110 Ok(())
111 }
112
113 fn get(&mut self, key: &str) -> Result<Option<serde_json::Value>, Self::GetError> {
114 Ok(self.data.get(key).map(Clone::clone))
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 #[test]
121 fn it_works() {
122 assert_eq!(2 + 2, 4);
123 }
124}