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
//! Snapshot persistence for derived view state.
use DeserializeOwned;
use ;
use fs;
use ;
use Path;
/// A persisted checkpoint of a view's state.
///
/// Snapshots are written atomically to disk (via a `.tmp` + rename) as a side
/// effect of [`View::refresh`](crate::View::refresh). They enable incremental
/// reads — on the next refresh, only events after `offset` need to be processed.
///
/// The snapshot file is JSON and can be inspected directly:
///
/// ```text
/// $ cat views/todos.snapshot.json | jq .
/// {
/// "state": { "items": [...], "next_id": 3 },
/// "offset": 1284,
/// "hash": "a3f2e1b09c4d..."
/// }
/// ```
/// Save a snapshot atomically to disk.
///
/// Writes to a `.tmp` file first, syncs, then renames to the final path.
/// If the process crashes mid-write, the old snapshot file survives intact.
///
/// # Examples
/// ```
/// # use tempfile::tempdir;
/// use eventfold::{snapshot, Snapshot};
/// # let dir = tempdir()?;
/// # let path = dir.path().join("test.snapshot.json");
/// let snap = Snapshot::new(42u64, 1024, "hash".to_string());
/// snapshot::save(&path, &snap)?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// # Errors
///
/// Returns an error if serialization fails or if writing/renaming the
/// file fails (permissions, disk full, etc.).
/// Load a snapshot from disk.
///
/// Returns `Ok(None)` if the file doesn't exist or if deserialization fails
/// (treating a corrupt snapshot as missing triggers a full rebuild).
///
/// # Examples
/// ```
/// # use tempfile::tempdir;
/// use eventfold::{snapshot, Snapshot};
/// # let dir = tempdir()?;
/// # let path = dir.path().join("test.snapshot.json");
/// # let snap = Snapshot::new(42u64, 0, String::new());
/// # snapshot::save(&path, &snap)?;
/// let loaded: Option<Snapshot<u64>> = snapshot::load(&path)?;
/// assert!(loaded.is_some());
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// # Errors
///
/// Returns an error on I/O failures other than `NotFound` (e.g., permission denied).
/// Delete a snapshot file and its `.tmp` file if present.
///
/// Idempotent — does not error if the files don't exist.
///
/// # Examples
/// ```
/// # use tempfile::tempdir;
/// use eventfold::{snapshot, Snapshot};
/// # let dir = tempdir()?;
/// # let path = dir.path().join("test.snapshot.json");
/// # let snap = Snapshot::new(42u64, 0, String::new());
/// # snapshot::save(&path, &snap)?;
/// snapshot::delete(&path)?;
/// let loaded: Option<Snapshot<u64>> = snapshot::load(&path)?;
/// assert!(loaded.is_none());
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// # Errors
///
/// Returns an error on I/O failures other than `NotFound` (e.g., permission denied).