fakecloud_persistence/
snapshot.rs1use std::future::Future;
2use std::io;
3use std::path::{Path, PathBuf};
4use std::pin::Pin;
5use std::sync::Arc;
6
7pub type SnapshotHook = Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
19
20pub trait SnapshotStore: Send + Sync {
27 fn load(&self) -> io::Result<Option<Vec<u8>>>;
30
31 fn save(&self, bytes: &[u8]) -> io::Result<()>;
34}
35
36pub struct MemorySnapshotStore;
39
40impl MemorySnapshotStore {
41 pub fn new() -> Self {
42 Self
43 }
44}
45
46impl Default for MemorySnapshotStore {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl SnapshotStore for MemorySnapshotStore {
53 fn load(&self) -> io::Result<Option<Vec<u8>>> {
54 Ok(None)
55 }
56
57 fn save(&self, _bytes: &[u8]) -> io::Result<()> {
58 Ok(())
59 }
60}
61
62pub struct DiskSnapshotStore {
66 path: PathBuf,
67}
68
69impl DiskSnapshotStore {
70 pub fn new(path: PathBuf) -> Self {
71 Self { path }
72 }
73
74 pub fn path(&self) -> &Path {
75 &self.path
76 }
77}
78
79impl SnapshotStore for DiskSnapshotStore {
80 fn load(&self) -> io::Result<Option<Vec<u8>>> {
81 match std::fs::read(&self.path) {
82 Ok(bytes) => Ok(Some(bytes)),
83 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
84 Err(err) => Err(err),
85 }
86 }
87
88 fn save(&self, bytes: &[u8]) -> io::Result<()> {
89 if let Some(parent) = self.path.parent() {
90 std::fs::create_dir_all(parent)?;
91 }
92 crate::atomic::write_atomic_bytes(&self.path, bytes)
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn memory_store_is_noop() {
102 let store = MemorySnapshotStore::new();
103 assert!(store.load().unwrap().is_none());
104 store.save(b"anything").unwrap();
105 assert!(store.load().unwrap().is_none());
106 }
107
108 #[test]
109 fn disk_store_round_trips() {
110 let tmp = tempfile::tempdir().unwrap();
111 let store = DiskSnapshotStore::new(tmp.path().join("sub/dir/snapshot.json"));
112 assert!(store.load().unwrap().is_none());
113 store.save(b"hello world").unwrap();
114 assert_eq!(store.load().unwrap().unwrap(), b"hello world");
115 store.save(b"second write").unwrap();
116 assert_eq!(store.load().unwrap().unwrap(), b"second write");
117 }
118}