cranpose_core/snapshot_v2/
readonly.rs1use super::*;
4
5#[allow(clippy::arc_with_non_send_sync)]
15pub struct ReadonlySnapshot {
16 state: SnapshotState,
17}
18
19impl ReadonlySnapshot {
20 pub fn new(
22 id: SnapshotId,
23 invalid: SnapshotIdSet,
24 read_observer: Option<ReadObserver>,
25 ) -> Arc<Self> {
26 Arc::new(Self {
27 state: SnapshotState::new(id, invalid, read_observer, None, false),
28 })
29 }
30
31 pub fn snapshot_id(&self) -> SnapshotId {
32 self.state.id.get()
33 }
34
35 pub fn invalid(&self) -> SnapshotIdSet {
36 self.state.invalid.borrow().clone()
37 }
38
39 pub fn read_only(&self) -> bool {
40 true
41 }
42
43 pub fn root_readonly(&self) -> Arc<Self> {
44 ReadonlySnapshot::new(
46 self.state.id.get(),
47 self.state.invalid.borrow().clone(),
48 self.state.read_observer.clone(),
49 )
50 }
51
52 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
53 let previous = current_snapshot();
54 set_current_snapshot(Some(AnySnapshot::Readonly(self.root_readonly())));
55 let result = f();
56 set_current_snapshot(previous);
57 result
58 }
59
60 pub fn take_nested_snapshot(&self, read_observer: Option<ReadObserver>) -> Arc<Self> {
61 let merged_observer = merge_read_observers(read_observer, self.state.read_observer.clone());
62 ReadonlySnapshot::new(
63 self.state.id.get(),
64 self.state.invalid.borrow().clone(),
65 merged_observer,
66 )
67 }
68
69 pub fn has_pending_changes(&self) -> bool {
70 false }
72
73 pub fn dispose(&self) {
74 self.state.dispose();
75 }
76
77 pub fn record_read(&self, state: &dyn StateObject) {
78 self.state.record_read(state);
79 }
80
81 pub fn record_write(&self, _state: Arc<dyn StateObject>) {
82 panic!("Cannot write to a read-only snapshot");
83 }
84
85 pub fn is_disposed(&self) -> bool {
86 self.state.disposed.get()
87 }
88
89 pub(crate) fn set_on_dispose<F>(&self, f: F)
91 where
92 F: FnOnce() + 'static,
93 {
94 self.state.set_on_dispose(f);
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::state::StateObject;
102 use std::cell::Cell;
103 use std::rc::Rc;
104
105 #[allow(dead_code)]
107 struct MockStateObject {
108 value: Cell<i32>,
109 }
110
111 impl StateObject for MockStateObject {
112 fn object_id(&self) -> crate::state::ObjectId {
113 crate::state::ObjectId(0)
114 }
115
116 fn first_record(&self) -> Rc<crate::state::StateRecord> {
117 unimplemented!("Not needed for tests")
118 }
119
120 fn readable_record(
121 &self,
122 _snapshot_id: crate::snapshot_id_set::SnapshotId,
123 _invalid: &SnapshotIdSet,
124 ) -> Rc<crate::state::StateRecord> {
125 unimplemented!("Not needed for tests")
126 }
127
128 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {
129 unimplemented!("Not needed for tests")
130 }
131
132 fn promote_record(
133 &self,
134 _child_id: crate::snapshot_id_set::SnapshotId,
135 ) -> Result<(), &'static str> {
136 unimplemented!("Not needed for tests")
137 }
138
139 fn as_any(&self) -> &dyn std::any::Any {
140 self
141 }
142 }
143
144 #[test]
145 fn test_readonly_snapshot_creation() {
146 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
147 assert_eq!(snapshot.snapshot_id(), 1);
148 assert!(!snapshot.is_disposed());
149 }
150
151 #[test]
152 fn test_readonly_snapshot_is_valid() {
153 let invalid = SnapshotIdSet::new().set(5);
154 let snapshot = ReadonlySnapshot::new(10, invalid, None);
155
156 let any_snapshot = AnySnapshot::Readonly(snapshot.clone());
157 assert!(any_snapshot.is_valid(1));
158 assert!(any_snapshot.is_valid(10));
159 assert!(!any_snapshot.is_valid(5)); assert!(!any_snapshot.is_valid(11)); }
162
163 #[test]
164 fn test_readonly_snapshot_no_pending_changes() {
165 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
166 assert!(!snapshot.has_pending_changes());
167 }
168
169 #[test]
170 fn test_readonly_snapshot_enter() {
171 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
172
173 set_current_snapshot(None);
174 assert!(current_snapshot().is_none());
175
176 snapshot.enter(|| {
177 let current = current_snapshot();
178 assert!(current.is_some());
179 assert_eq!(current.unwrap().snapshot_id(), 1);
180 });
181
182 assert!(current_snapshot().is_none());
183 }
184
185 #[test]
186 fn test_readonly_snapshot_enter_restores_previous() {
187 let snapshot1 = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
188 let snapshot2 = ReadonlySnapshot::new(2, SnapshotIdSet::new(), None);
189
190 snapshot1.enter(|| {
191 snapshot2.enter(|| {
192 let current = current_snapshot();
193 assert_eq!(current.unwrap().snapshot_id(), 2);
194 });
195
196 let current = current_snapshot();
197 assert_eq!(current.unwrap().snapshot_id(), 1);
198 });
199 }
200
201 #[test]
202 fn test_readonly_snapshot_nested() {
203 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
204 let nested = parent.take_nested_snapshot(None);
205
206 assert_eq!(nested.snapshot_id(), 1); }
208
209 #[test]
210 fn test_readonly_snapshot_read_observer() {
211 use std::sync::{Arc as StdArc, Mutex};
212
213 let read_count = StdArc::new(Mutex::new(0));
214 let read_count_clone = read_count.clone();
215
216 let observer = Arc::new(move |_: &dyn StateObject| {
217 *read_count_clone.lock().unwrap() += 1;
218 });
219
220 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(observer));
221 let mock_state = MockStateObject {
222 value: Cell::new(42),
223 };
224
225 snapshot.record_read(&mock_state);
226 snapshot.record_read(&mock_state);
227
228 assert_eq!(*read_count.lock().unwrap(), 2);
229 }
230
231 #[test]
232 fn test_readonly_snapshot_nested_with_observer() {
233 use std::sync::{Arc as StdArc, Mutex};
234
235 let parent_reads = StdArc::new(Mutex::new(0));
236 let parent_reads_clone = parent_reads.clone();
237 let parent_observer = Arc::new(move |_: &dyn StateObject| {
238 *parent_reads_clone.lock().unwrap() += 1;
239 });
240
241 let nested_reads = StdArc::new(Mutex::new(0));
242 let nested_reads_clone = nested_reads.clone();
243 let nested_observer = Arc::new(move |_: &dyn StateObject| {
244 *nested_reads_clone.lock().unwrap() += 1;
245 });
246
247 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(parent_observer));
248 let nested = parent.take_nested_snapshot(Some(nested_observer));
249
250 let mock_state = MockStateObject {
251 value: Cell::new(42),
252 };
253
254 nested.record_read(&mock_state);
256
257 assert_eq!(*parent_reads.lock().unwrap(), 1);
258 assert_eq!(*nested_reads.lock().unwrap(), 1);
259 }
260
261 #[test]
262 #[should_panic(expected = "Cannot write to a read-only snapshot")]
263 fn test_readonly_snapshot_write_panics() {
264 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
265 let mock_state = Arc::new(MockStateObject {
266 value: Cell::new(42),
267 });
268 snapshot.record_write(mock_state);
269 }
270
271 #[test]
272 fn test_readonly_snapshot_dispose() {
273 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
274 assert!(!snapshot.is_disposed());
275
276 snapshot.dispose();
277 assert!(snapshot.is_disposed());
278 }
279}