1use std::{
2 any::{Any, TypeId},
3 cell::{Cell, RefCell},
4 hash::{Hash, Hasher},
5 rc::{Rc, Weak},
6};
7
8use smallvec::SmallVec;
9
10use crate::{
11 collections::map::{HashMap, HashSet},
12 hash::default as default_hash,
13 snapshot_v2::{
14 ReadObserver, StateObjectId, TransparentObserverMutableSnapshot, register_apply_observer,
15 },
16 state::StateObject,
17};
18
19type Executor = dyn Fn(Box<dyn FnOnce() + 'static>) + 'static;
20
21trait ScopeChangedCallback: Fn(&dyn Any) + Any {}
22
23impl<F: Fn(&dyn Any) + Any> ScopeChangedCallback for F {}
24
25#[derive(Clone)]
38pub struct SnapshotStateObserver {
39 inner: Rc<SnapshotStateObserverInner>,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43pub struct SnapshotStateObserverDebugStats {
44 pub scopes_len: usize,
45 pub scopes_cap: usize,
46 pub stateless_scope_count: usize,
47 pub observed_state_count: usize,
48 pub observed_state_capacity: usize,
49}
50
51impl SnapshotStateObserver {
52 pub fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
54 Self {
55 inner: Rc::new(SnapshotStateObserverInner::new(on_changed_executor)),
56 }
57 }
58
59 pub fn observe_reads<T, R>(
65 &self,
66 scope: T,
67 on_value_changed_for_scope: impl Fn(&T) + 'static,
68 block: impl FnOnce() -> R,
69 ) -> R
70 where
71 T: Any + Clone + Eq + Hash + 'static,
72 {
73 self.inner
74 .observe_reads(scope, on_value_changed_for_scope, block)
75 }
76
77 pub fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
79 self.inner.with_no_observations(block)
80 }
81
82 pub fn clear<T>(&self, scope: &T)
84 where
85 T: Any + Eq + Hash + 'static,
86 {
87 self.inner.clear(scope);
88 }
89
90 pub fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
92 self.inner.clear_if(predicate);
93 }
94
95 pub fn clear_all(&self) {
97 self.inner.clear_all();
98 }
99
100 pub fn start(&self) {
102 let weak = Rc::downgrade(&self.inner);
103 self.inner.start(weak);
104 }
105
106 pub fn stop(&self) {
108 self.inner.stop();
109 }
110
111 pub fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
112 self.inner.debug_stats()
113 }
114
115 #[cfg(test)]
116 pub fn notify_changes(&self, modified: &[Rc<dyn StateObject>]) {
117 self.inner.handle_apply(modified);
118 }
119}
120
121struct SnapshotStateObserverInner {
122 executor: Rc<Executor>,
123 owned_scopes: RefCell<HashMap<OwnedScopeIndexKey, OwnedScopeBucket>>,
124 indexed_scopes: RefCell<HashMap<usize, Rc<RefCell<ScopeEntry>>>>,
125 observed_to_scopes: RefCell<HashMap<StateObjectId, HashSet<usize>>>,
126 pause_count: Rc<Cell<usize>>,
127 active_read_targets: Rc<RefCell<ReadObservationStack>>,
128 read_dispatcher: ReadObserver,
129 read_snapshot: RefCell<Option<Rc<TransparentObserverMutableSnapshot>>>,
130 apply_handle: RefCell<Option<crate::snapshot_v2::ObserverHandle>>,
131 next_entry_id: Cell<usize>,
132 capture_free_callbacks: RefCell<CaptureFreeCallbacks>,
135}
136
137#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
138struct OwnedScopeIndexKey {
139 type_id: TypeId,
140 value_hash: u64,
141}
142
143type OwnedScopeBucket = SmallVec<[Rc<RefCell<ScopeEntry>>; 1]>;
144
145type CaptureFreeCallbacks = SmallVec<[(TypeId, Rc<dyn ScopeChangedCallback>); 2]>;
147
148fn owned_scope_index_key<T>(scope: &T) -> OwnedScopeIndexKey
149where
150 T: Any + Hash + 'static,
151{
152 let mut hasher = default_hash::new();
153 scope.hash(&mut hasher);
154 OwnedScopeIndexKey {
155 type_id: TypeId::of::<T>(),
156 value_hash: hasher.finish(),
157 }
158}
159
160impl SnapshotStateObserverInner {
161 const MIN_RETAINED_SCOPE_CAPACITY: usize = 256;
162
163 fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
164 let pause_count = Rc::new(Cell::new(0));
165 let active_read_targets = Rc::new(RefCell::new(ReadObservationStack::default()));
166 let dispatcher_pause_count = Rc::clone(&pause_count);
167 let dispatcher_targets = Rc::clone(&active_read_targets);
168 let read_dispatcher: ReadObserver = Rc::new(move |state| {
169 if dispatcher_pause_count.get() > 0 {
170 return;
171 }
172 let observed = dispatcher_targets.borrow().last().cloned();
173 if let Some(observed) = observed {
174 observed.borrow_mut().insert(state);
175 }
176 });
177
178 Self {
179 executor: Rc::new(on_changed_executor),
180 owned_scopes: RefCell::new(HashMap::default()),
181 indexed_scopes: RefCell::new(HashMap::default()),
182 observed_to_scopes: RefCell::new(HashMap::default()),
183 pause_count,
184 active_read_targets,
185 read_dispatcher,
186 read_snapshot: RefCell::new(None),
187 apply_handle: RefCell::new(None),
188 next_entry_id: Cell::new(0),
189 capture_free_callbacks: RefCell::new(SmallVec::new()),
190 }
191 }
192
193 fn observe_reads<T, R>(
194 &self,
195 scope: T,
196 on_value_changed_for_scope: impl Fn(&T) + 'static,
197 block: impl FnOnce() -> R,
198 ) -> R
199 where
200 T: Any + Clone + Eq + Hash + 'static,
201 {
202 let existing_entry = self.find_scope_entry(&scope);
203 let on_changed = std::cell::LazyCell::new(|| {
204 let callback = move |scope_any: &dyn Any| {
205 if let Some(typed) = scope_any.downcast_ref::<T>() {
206 on_value_changed_for_scope(typed);
207 }
208 };
209 if std::mem::size_of_val(&callback) == 0 {
210 return self.capture_free_callback(callback);
211 }
212 match existing_entry.as_ref() {
213 Some(entry) => entry.borrow_mut().callback_reusing(callback),
214 None => Rc::new(callback),
215 }
216 });
217
218 if let Some(entry) = existing_entry.as_ref() {
219 entry.borrow_mut().update_scope(&scope);
220 let callback = on_changed.clone();
221 entry.borrow_mut().on_changed = callback;
222 }
223
224 let observed = self.active_read_targets.borrow_mut().push();
225 struct ActiveObservationGuard<'a> {
226 stack: &'a RefCell<ReadObservationStack>,
227 }
228 impl Drop for ActiveObservationGuard<'_> {
229 fn drop(&mut self) {
230 let target = self.stack.borrow_mut().pop();
231 target.borrow_mut().clear();
232 }
233 }
234 let _guard = ActiveObservationGuard {
235 stack: &self.active_read_targets,
236 };
237
238 let result = self.run_with_read_observer(block);
239
240 if observed.borrow().is_empty() {
241 if existing_entry.is_some() {
242 self.clear(&scope);
243 }
244 return result;
245 }
246
247 let callback = Rc::clone(&on_changed);
248 drop(on_changed);
249 let entry = existing_entry
250 .unwrap_or_else(|| self.insert_scope_entry(scope.clone(), Rc::clone(&callback)));
251 entry.borrow_mut().update(&scope, callback);
252 self.replace_observed_ids(&entry, &mut observed.borrow_mut());
253
254 result
255 }
256
257 fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
258 self.pause_count.set(self.pause_count.get() + 1);
259 let result = block();
260 self.pause_count
261 .set(self.pause_count.get().saturating_sub(1));
262 result
263 }
264
265 fn clear<T>(&self, scope: &T)
266 where
267 T: Any + Eq + Hash + 'static,
268 {
269 let removed = self.remove_scope_entry(scope);
270 if let Some(entry) = removed {
271 self.unregister_entry(&entry);
272 }
273 }
274
275 fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
276 let removed = self.partition_scopes(|entry| predicate(entry.scope.as_ref()));
277 for entry in removed {
278 self.unregister_entry(&entry);
279 }
280 }
281
282 fn clear_all(&self) {
283 let entries = std::mem::take(&mut *self.indexed_scopes.borrow_mut());
284 let owned = std::mem::take(&mut *self.owned_scopes.borrow_mut());
285 self.observed_to_scopes.borrow_mut().clear();
286 drop((entries, owned));
287 }
288
289 fn start(&self, weak_self: Weak<SnapshotStateObserverInner>) {
290 if self.apply_handle.borrow().is_some() {
291 return;
292 }
293
294 let handle = register_apply_observer(Rc::new(move |modified, _snapshot_id| {
295 if let Some(inner) = weak_self.upgrade() {
296 inner.handle_apply(modified);
297 }
298 }));
299 self.apply_handle.replace(Some(handle));
300 }
301
302 fn stop(&self) {
303 if let Some(handle) = self.apply_handle.borrow_mut().take() {
304 drop(handle);
305 }
306 }
307
308 fn capture_free_callback<F: Fn(&dyn Any) + 'static>(
312 &self,
313 callback: F,
314 ) -> Rc<dyn ScopeChangedCallback> {
315 let type_id = TypeId::of::<F>();
316 let mut shared = self.capture_free_callbacks.borrow_mut();
317 if let Some((_, callback)) = shared.iter().find(|(id, _)| *id == type_id) {
318 return Rc::clone(callback);
319 }
320 let callback: Rc<dyn ScopeChangedCallback> = Rc::new(callback);
321 shared.push((type_id, Rc::clone(&callback)));
322 callback
323 }
324
325 fn insert_scope_entry(
326 &self,
327 scope: impl Any + Clone + Eq + Hash + 'static,
328 on_changed: Rc<dyn ScopeChangedCallback>,
329 ) -> Rc<RefCell<ScopeEntry>> {
330 let entry_id = self.next_entry_id.get();
331 self.next_entry_id.set(entry_id.wrapping_add(1));
332 let scope_key = owned_scope_index_key(&scope);
333 let entry = Rc::new(RefCell::new(ScopeEntry::new(entry_id, scope, on_changed)));
334 self.indexed_scopes
335 .borrow_mut()
336 .insert(entry_id, Rc::clone(&entry));
337 self.owned_scopes
338 .borrow_mut()
339 .entry(scope_key)
340 .or_default()
341 .push(Rc::clone(&entry));
342 entry
343 }
344
345 fn find_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
346 where
347 T: Any + Eq + Hash + 'static,
348 {
349 let key = owned_scope_index_key(scope);
350 self.owned_scopes.borrow().get(&key).and_then(|bucket| {
351 bucket
352 .iter()
353 .find(|entry| entry.borrow().matches_scope(scope))
354 .cloned()
355 })
356 }
357
358 fn remove_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
359 where
360 T: Any + Eq + Hash + 'static,
361 {
362 let key = owned_scope_index_key(scope);
363 let mut owned_scopes = self.owned_scopes.borrow_mut();
364 let mut removed = None;
365 let mut remove_bucket = false;
366 if let Some(bucket) = owned_scopes.get_mut(&key)
367 && let Some(index) = bucket
368 .iter()
369 .position(|entry| entry.borrow().matches_scope(scope))
370 {
371 removed = Some(bucket.remove(index));
372 remove_bucket = bucket.is_empty();
373 }
374 if remove_bucket {
375 owned_scopes.remove(&key);
376 }
377 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
378 removed
379 }
380
381 fn partition_scopes(
382 &self,
383 should_remove: impl Fn(&ScopeEntry) -> bool,
384 ) -> Vec<Rc<RefCell<ScopeEntry>>> {
385 let mut owned_scopes = self.owned_scopes.borrow_mut();
386 let mut removed = Vec::new();
387 owned_scopes.retain(|_, bucket| {
388 let mut index = 0;
389 while index < bucket.len() {
390 let remove = should_remove(&bucket[index].borrow());
391 if remove {
392 removed.push(bucket.swap_remove(index));
393 } else {
394 index += 1;
395 }
396 }
397 !bucket.is_empty()
398 });
399 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
400 removed
401 }
402
403 fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
404 let owned_scopes = self.owned_scopes.borrow();
405 let indexed_scopes = self.indexed_scopes.borrow();
406 let owned_scope_cap =
407 owned_scopes.capacity() + owned_scopes.values().map(SmallVec::capacity).sum::<usize>();
408 let scopes_cap = owned_scope_cap + indexed_scopes.capacity();
409 let mut observed_state_count = 0;
410 let mut observed_state_capacity = 0;
411 let mut stateless_scope_count = 0;
412
413 for entry in indexed_scopes.values() {
414 let entry = entry.borrow();
415 observed_state_count += entry.observed.len();
416 observed_state_capacity += entry.observed.capacity();
417 stateless_scope_count += usize::from(entry.observed.is_empty());
418 }
419
420 SnapshotStateObserverDebugStats {
421 scopes_len: indexed_scopes.len(),
422 scopes_cap,
423 stateless_scope_count,
424 observed_state_count,
425 observed_state_capacity,
426 }
427 }
428
429 fn run_with_read_observer<R>(&self, block: impl FnOnce() -> R) -> R {
430 use crate::snapshot_v2::{
431 current_snapshot_reads_into, take_transparent_observer_mutable_snapshot_reusing,
432 };
433
434 if current_snapshot_reads_into(&self.read_dispatcher) {
435 return block();
436 }
437
438 let mut snapshot = take_transparent_observer_mutable_snapshot_reusing(
439 Some(self.read_dispatcher.clone()),
440 None,
441 self.read_snapshot.take(),
442 );
443 let result = snapshot.enter(block);
444 snapshot.dispose();
445 if Rc::get_mut(&mut snapshot).is_some() && !snapshot.has_pending_changes() {
446 self.read_snapshot.replace(Some(snapshot));
447 }
448 result
449 }
450
451 fn handle_apply(&self, modified: &[Rc<dyn StateObject>]) {
452 if modified.is_empty() {
453 return;
454 }
455
456 let mut seen_scope_ids: HashSet<usize> = HashSet::default();
457 let mut to_notify: Vec<Rc<RefCell<ScopeEntry>>> = Vec::new();
458 {
459 let observed_to_scopes = self.observed_to_scopes.borrow();
460 let indexed_scopes = self.indexed_scopes.borrow();
461 for state in modified {
462 if let Some(scope_ids) = observed_to_scopes.get(&state.object_id().as_usize()) {
463 let mut ordered_scope_ids: SmallVec<[usize; 8]> =
464 scope_ids.iter().copied().collect();
465 ordered_scope_ids.sort_unstable();
466 for scope_id in ordered_scope_ids {
467 if seen_scope_ids.insert(scope_id)
468 && let Some(entry) = indexed_scopes.get(&scope_id)
469 {
470 to_notify.push(entry.clone());
471 }
472 }
473 }
474 }
475 }
476
477 if to_notify.is_empty() {
478 return;
479 }
480
481 for entry in to_notify {
482 let executor = self.executor.clone();
483 executor(Box::new(move || {
484 if let Ok(entry) = entry.try_borrow() {
485 entry.notify();
486 }
487 }));
488 }
489 }
490
491 fn replace_observed_ids(&self, entry: &Rc<RefCell<ScopeEntry>>, collected: &mut ObservedIds) {
492 let (entry_id, previous) = {
493 let mut entry_mut = entry.borrow_mut();
494 if entry_mut.observed.iter().eq(collected.iter()) {
495 entry_mut.observed.take_leases(collected);
496 collected.clear();
497 return;
498 }
499 let entry_id = entry_mut.id;
500 let previous = std::mem::replace(&mut entry_mut.observed, collected.take_sized());
501 (entry_id, previous)
502 };
503 let entry_ref = entry.borrow();
504 self.unregister_observed_ids(entry_id, &previous);
505 self.register_observed_ids(entry_id, &entry_ref.observed);
506 }
507
508 fn register_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
509 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
510 for state_id in observed.iter() {
511 let scope_ids = observed_to_scopes.entry(state_id).or_default();
512 scope_ids.insert(entry_id);
513 }
514 }
515
516 fn unregister_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
517 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
518 let mut emptied = SmallVec::<[StateObjectId; MAX_OBSERVED_STATES]>::new();
519 for state_id in observed.iter() {
520 if let Some(scope_ids) = observed_to_scopes.get_mut(&state_id) {
521 scope_ids.remove(&entry_id);
522 if scope_ids.is_empty() {
523 emptied.push(state_id);
524 }
525 }
526 }
527 for state_id in emptied {
528 observed_to_scopes.remove(&state_id);
529 }
530 shrink_map_if_sparse(&mut observed_to_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
531 }
532
533 fn unregister_entry(&self, entry: &Rc<RefCell<ScopeEntry>>) {
534 let (entry_id, observed) = {
535 let mut entry_mut = entry.borrow_mut();
536 let observed = std::mem::replace(&mut entry_mut.observed, ObservedIds::new());
537 (entry_mut.id, observed)
538 };
539 self.unregister_observed_ids(entry_id, &observed);
540 self.indexed_scopes.borrow_mut().remove(&entry_id);
541 }
542}
543
544fn shrink_map_if_sparse<K, V>(map: &mut HashMap<K, V>, min_retained_capacity: usize)
545where
546 K: Eq + std::hash::Hash,
547{
548 if map.capacity() <= map.len().max(min_retained_capacity).saturating_mul(4) {
549 return;
550 }
551
552 let retained = map.len().max(min_retained_capacity);
553 let mut rebuilt = HashMap::default();
554 rebuilt.reserve(retained);
555 rebuilt.extend(map.drain());
556 *map = rebuilt;
557}
558
559#[derive(Default)]
560struct ReadObservationStack {
561 targets: Vec<Rc<RefCell<ObservedIds>>>,
562 depth: usize,
563}
564
565impl ReadObservationStack {
566 fn push(&mut self) -> Rc<RefCell<ObservedIds>> {
567 if self.depth == self.targets.len() {
568 self.targets.push(Rc::new(RefCell::new(ObservedIds::new())));
569 }
570 let target = Rc::clone(&self.targets[self.depth]);
571 self.depth += 1;
572 target
573 }
574
575 fn last(&self) -> Option<&Rc<RefCell<ObservedIds>>> {
576 self.depth.checked_sub(1).map(|index| &self.targets[index])
577 }
578
579 fn pop(&mut self) -> Rc<RefCell<ObservedIds>> {
580 self.depth -= 1;
581 Rc::clone(&self.targets[self.depth])
582 }
583}
584
585enum ObservedIds {
586 Small(SmallVec<[ObservedState; 1]>),
587 Large(Box<HashMap<StateObjectId, Option<Rc<dyn Any>>>>),
588}
589
590struct ObservedState {
591 id: StateObjectId,
592 _lease: Option<Rc<dyn Any>>,
593}
594
595impl ObservedIds {
596 fn new() -> Self {
597 ObservedIds::Small(SmallVec::new())
598 }
599
600 fn insert(&mut self, state: &dyn StateObject) {
601 let id = state.object_id().as_usize();
602 match self {
603 ObservedIds::Small(small) => {
604 if small.iter().any(|observed| observed.id == id) {
605 return;
606 }
607 if small.len() < MAX_OBSERVED_STATES {
608 small.push(ObservedState {
609 id,
610 _lease: state.observation_lease(),
611 });
612 } else {
613 let mut large =
614 HashMap::with_capacity_and_hasher(small.len() + 1, Default::default());
615 for observed in small.drain(..) {
616 large.insert(observed.id, observed._lease);
617 }
618 large.insert(id, state.observation_lease());
619 *self = ObservedIds::Large(Box::new(large));
620 }
621 }
622 ObservedIds::Large(large) => {
623 large.entry(id).or_insert_with(|| state.observation_lease());
624 }
625 }
626 }
627
628 fn clear(&mut self) {
629 match self {
630 ObservedIds::Small(small) => small.clear(),
631 ObservedIds::Large(_) => *self = ObservedIds::new(),
632 }
633 }
634
635 fn take_leases(&mut self, fresh: &mut ObservedIds) {
640 match (self, fresh) {
641 (ObservedIds::Small(kept), ObservedIds::Small(fresh)) => {
642 for (kept, fresh) in kept.iter_mut().zip(fresh.iter_mut()) {
643 std::mem::swap(&mut kept._lease, &mut fresh._lease);
644 }
645 }
646 (kept, fresh) => std::mem::swap(kept, fresh),
647 }
648 }
649
650 fn take_sized(&mut self) -> ObservedIds {
651 match self {
652 ObservedIds::Small(small) => ObservedIds::Small(small.drain(..).collect()),
653 ObservedIds::Large(_) => std::mem::replace(self, ObservedIds::new()),
654 }
655 }
656
657 fn is_empty(&self) -> bool {
658 match self {
659 ObservedIds::Small(small) => small.is_empty(),
660 ObservedIds::Large(large) => large.is_empty(),
661 }
662 }
663
664 fn len(&self) -> usize {
665 match self {
666 ObservedIds::Small(small) => small.len(),
667 ObservedIds::Large(large) => large.len(),
668 }
669 }
670
671 fn capacity(&self) -> usize {
672 match self {
673 ObservedIds::Small(small) => small.capacity(),
674 ObservedIds::Large(large) => large.capacity(),
675 }
676 }
677
678 fn iter(&self) -> impl Iterator<Item = StateObjectId> + '_ {
679 let (small, large) = match self {
680 ObservedIds::Small(small) => (Some(small.as_slice()), None),
681 ObservedIds::Large(large) => (None, Some(large)),
682 };
683 small
684 .into_iter()
685 .flatten()
686 .map(|observed| observed.id)
687 .chain(large.into_iter().flat_map(|states| states.keys().copied()))
688 }
689}
690
691const MAX_OBSERVED_STATES: usize = 8;
692
693struct ScopeEntry {
694 id: usize,
695 scope: Box<dyn Any>,
696 on_changed: Rc<dyn ScopeChangedCallback>,
697 observed: ObservedIds,
698}
699
700impl ScopeEntry {
701 fn new<T>(id: usize, scope: T, on_changed: Rc<dyn ScopeChangedCallback>) -> Self
702 where
703 T: Any + 'static,
704 {
705 Self {
706 id,
707 scope: Box::new(scope),
708 on_changed,
709 observed: ObservedIds::new(),
710 }
711 }
712
713 fn callback_reusing<F: Fn(&dyn Any) + 'static>(
714 &mut self,
715 callback: F,
716 ) -> Rc<dyn ScopeChangedCallback> {
717 if let Some(stored) = Rc::get_mut(&mut self.on_changed)
718 .and_then(|stored| (stored as &mut dyn Any).downcast_mut::<F>())
719 {
720 *stored = callback;
721 Rc::clone(&self.on_changed)
722 } else {
723 Rc::new(callback)
724 }
725 }
726
727 fn update<T>(&mut self, new_scope: &T, on_changed: Rc<dyn ScopeChangedCallback>)
728 where
729 T: Any + Clone + 'static,
730 {
731 self.update_scope(new_scope);
732 self.on_changed = on_changed;
733 }
734
735 fn update_scope<T>(&mut self, new_scope: &T)
736 where
737 T: Any + Clone + 'static,
738 {
739 match self.scope.downcast_mut::<T>() {
740 Some(stored) => stored.clone_from(new_scope),
741 None => self.scope = Box::new(new_scope.clone()),
742 }
743 }
744
745 fn matches_scope<T>(&self, scope: &T) -> bool
746 where
747 T: Any + Eq + 'static,
748 {
749 self.scope
750 .downcast_ref::<T>()
751 .is_some_and(|stored| stored == scope)
752 }
753
754 fn notify(&self) {
755 (self.on_changed)(self.scope.as_ref());
756 }
757}
758
759#[cfg(test)]
760#[path = "tests/snapshot_state_observer_tests.rs"]
761mod tests;