fakecloud_persistence/
snapshot.rs1use std::io;
2use std::path::{Path, PathBuf};
3
4pub trait SnapshotStore: Send + Sync {
11 fn load(&self) -> io::Result<Option<Vec<u8>>>;
14
15 fn save(&self, bytes: &[u8]) -> io::Result<()>;
18}
19
20pub struct MemorySnapshotStore;
23
24impl MemorySnapshotStore {
25 pub fn new() -> Self {
26 Self
27 }
28}
29
30impl Default for MemorySnapshotStore {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl SnapshotStore for MemorySnapshotStore {
37 fn load(&self) -> io::Result<Option<Vec<u8>>> {
38 Ok(None)
39 }
40
41 fn save(&self, _bytes: &[u8]) -> io::Result<()> {
42 Ok(())
43 }
44}
45
46pub struct DiskSnapshotStore {
50 path: PathBuf,
51}
52
53impl DiskSnapshotStore {
54 pub fn new(path: PathBuf) -> Self {
55 Self { path }
56 }
57
58 pub fn path(&self) -> &Path {
59 &self.path
60 }
61}
62
63impl SnapshotStore for DiskSnapshotStore {
64 fn load(&self) -> io::Result<Option<Vec<u8>>> {
65 match std::fs::read(&self.path) {
66 Ok(bytes) => Ok(Some(bytes)),
67 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
68 Err(err) => Err(err),
69 }
70 }
71
72 fn save(&self, bytes: &[u8]) -> io::Result<()> {
73 if let Some(parent) = self.path.parent() {
74 std::fs::create_dir_all(parent)?;
75 }
76 crate::atomic::write_atomic_bytes(&self.path, bytes)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn memory_store_is_noop() {
86 let store = MemorySnapshotStore::new();
87 assert!(store.load().unwrap().is_none());
88 store.save(b"anything").unwrap();
89 assert!(store.load().unwrap().is_none());
90 }
91
92 #[test]
93 fn disk_store_round_trips() {
94 let tmp = tempfile::tempdir().unwrap();
95 let store = DiskSnapshotStore::new(tmp.path().join("sub/dir/snapshot.json"));
96 assert!(store.load().unwrap().is_none());
97 store.save(b"hello world").unwrap();
98 assert_eq!(store.load().unwrap().unwrap(), b"hello world");
99 store.save(b"second write").unwrap();
100 assert_eq!(store.load().unwrap().unwrap(), b"second write");
101 }
102}