1use std::fmt::{self, Debug};
6use std::ops::Deref;
7use std::sync::Mutex;
8
9use crate::DropWatch;
10
11pub struct Vigil<T: ?Sized> {
13 ptr: Box<VigilInner<T>>,
14}
15
16impl<T: ?Sized> Vigil<T> {
17 fn from_inner(ptr: Box<VigilInner<T>>) -> Self {
18 Self { ptr }
19 }
20
21 fn inner(&self) -> &VigilInner<T> {
22 &self.ptr
23 }
24}
25
26impl<T> Vigil<T> {
27 pub fn new(data: T) -> Vigil<T> {
28 let x = Box::new(VigilInner { observers: Mutex::new(Vec::new()), data });
29 Self::from_inner(x)
30 }
31}
32
33impl<T: ?Sized> DropWatch<T> for Vigil<T> {
34 fn watch(this: &Self, drop_fn: impl FnOnce(&T) + 'static) {
35 this.inner().observers.lock().unwrap().push(Box::new(drop_fn));
36 }
37}
38
39#[derive(Default)]
40struct VigilInner<T: ?Sized> {
41 observers: Mutex<Vec<Box<dyn FnOnce(&T)>>>,
42 data: T,
43}
44
45impl<T: ?Sized> Drop for Vigil<T> {
46 fn drop(&mut self) {
47 for rite in self.inner().observers.lock().unwrap().drain(..) {
48 (rite)(&**self);
49 }
50 }
51}
52
53impl<T: ?Sized> Deref for Vigil<T> {
54 type Target = T;
55
56 fn deref(&self) -> &Self::Target {
57 &self.inner().data
58 }
59}
60
61impl<T: fuchsia_inspect_derive::Unit> fuchsia_inspect_derive::Unit for Vigil<T> {
62 type Data = T::Data;
63 fn inspect_create(&self, parent: &fuchsia_inspect::Node, name: impl AsRef<str>) -> Self::Data {
64 self.inner().data.inspect_create(parent, name)
65 }
66
67 fn inspect_update(&self, data: &mut Self::Data) {
68 self.inner().data.inspect_update(data)
69 }
70}
71
72impl<T: Debug> Debug for Vigil<T> {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "Vigil({:?})", self.inner().data)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use std::rc::Rc;
81 use std::sync::atomic::{AtomicUsize, Ordering};
82
83 use super::*;
84
85 #[test]
86 fn rites_upon_drop() {
87 let ghosts = Rc::new(Mutex::new(String::new()));
88
89 let v = Vigil::new('👻');
90
91 Vigil::watch(&v, {
92 let ghosts = ghosts.clone();
93 move |thing| ghosts.lock().unwrap().push(thing.clone())
94 });
95
96 Vigil::watch(&v, {
97 let ghosts = ghosts.clone();
98 move |thing| ghosts.lock().unwrap().push(thing.clone())
99 });
100
101 drop(v);
102
103 let unwrapped = Rc::try_unwrap(ghosts).unwrap();
105
106 let ghosts_result = unwrapped.into_inner().unwrap();
107
108 assert_eq!("👻👻", ghosts_result);
109 }
110 struct DropIncrementer(Rc<AtomicUsize>);
111
112 impl Drop for DropIncrementer {
113 fn drop(&mut self) {
114 let _ = self.0.fetch_add(1, Ordering::Relaxed);
115 }
116 }
117
118 #[test]
119 fn drops_stored_data() {
120 let shared_count = Rc::new(AtomicUsize::new(0));
121 let v = Vigil::new(DropIncrementer(shared_count.clone()));
122 drop(v);
123 assert_eq!(1, shared_count.load(Ordering::Relaxed));
124 }
125}