exocore_core/simple_store/
json_disk_store.rs1use std::{
2 fs::OpenOptions,
3 io::{Error, ErrorKind},
4 path::{Path, PathBuf},
5};
6
7use super::*;
8
9pub struct JsonDiskStore<T: Serialize + DeserializeOwned> {
10 path: PathBuf,
11 phantom: std::marker::PhantomData<T>,
12}
13
14impl<T: Serialize + DeserializeOwned> JsonDiskStore<T> {
15 pub fn new<P: AsRef<Path>>(path: P) -> Result<JsonDiskStore<T>> {
16 let meta = std::fs::metadata(path.as_ref());
17 if meta.map(|m| m.is_dir()).unwrap_or(false) {
18 error!("Path {:?} is a directory, not a file.", path.as_ref());
19 return Err(Error::new(
20 ErrorKind::InvalidData,
21 "Path was a directory, not a file",
22 ));
23 }
24
25 Ok(JsonDiskStore {
26 path: path.as_ref().to_path_buf(),
27 phantom: std::marker::PhantomData,
28 })
29 }
30}
31
32impl<T: Serialize + DeserializeOwned> SimpleStore<T> for JsonDiskStore<T> {
33 fn read(&self) -> Result<Option<T>> {
34 let meta = std::fs::metadata(&self.path);
35 if meta.is_err() {
36 return Ok(None);
37 }
38
39 let file = OpenOptions::new()
40 .read(true)
41 .write(false)
42 .create(false)
43 .open(&self.path)?;
44
45 serde_json::from_reader(file).map_err(|err| {
46 error!("Couldn't decode stored JSON file: {}", err);
47 Error::new(ErrorKind::InvalidData, "Invalid json data")
48 })
49 }
50
51 fn write(&self, value: &T) -> Result<()> {
52 let tmp_file = self.path.with_extension("tmp");
53 let file = OpenOptions::new()
54 .read(false)
55 .write(true)
56 .create(true)
57 .truncate(true)
58 .open(&tmp_file)?;
59 serde_json::to_writer(file, value)?;
60
61 std::fs::rename(&tmp_file, &self.path)
62 }
63}
64
65#[cfg(test)]
66mod test {
67 use serde_derive::{Deserialize, Serialize};
68 use tempfile::tempdir;
69
70 use super::*;
71
72 #[test]
73 fn test_not_dir() {
74 let dir = tempdir().unwrap();
75 let store = JsonDiskStore::<String>::new(dir.path());
76 assert!(store.is_err());
77
78 let file = dir.path().join("file");
79 let store = JsonDiskStore::<String>::new(file);
80 assert!(store.is_ok());
81 }
82
83 #[test]
84 fn test_write_read() {
85 let dir = tempdir().unwrap();
86 let file = dir.path().join("store");
87 let store = JsonDiskStore::<TestStruct>::new(file).unwrap();
88
89 let data = TestStruct {
90 data: "hello".to_string(),
91 };
92 store.write(&data).unwrap();
93
94 let read_data = store.read().unwrap();
95 assert_eq!(read_data, Some(data));
96 }
97
98 #[derive(PartialEq, Debug, Serialize, Deserialize)]
99 struct TestStruct {
100 data: String,
101 }
102}