dropcatch/
lib.rs

1
2use std::{fmt::Debug, sync::Arc};
3
4#[derive(Clone)]
5pub struct DropCatcher<F>
6where F: FnOnce() + Send + 'static
7{
8    #[cfg(feature = "random-id")]
9    id: [u8; 16],
10    _inner: Arc<DropCatcherInner<F>>
11}
12
13impl<F> DropCatcher<F>
14where F: FnOnce() + Send + 'static
15{
16    pub fn new(f: F) -> Self {
17        Self {
18            #[cfg(feature = "random-id")]
19            id: rand::random(),
20
21            _inner: Arc::new(DropCatcherInner { f: Some(f) }),
22        }
23    }
24}
25
26impl<F> Debug for DropCatcher<F>
27where 
28    F: FnOnce() + Send + 'static
29{
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        f.write_str("DropCatcher")
32    }
33}
34
35#[cfg(feature = "random-id")]
36mod random_id_traits {
37    use std::hash::Hash;
38
39    use super::DropCatcher;
40
41    impl<F1, F2> PartialEq<DropCatcher<F2>> for DropCatcher<F1>
42    where
43        F1: FnOnce() + Send + 'static,
44        F2: FnOnce() + Send + 'static,
45    {
46        fn eq(&self, other: &DropCatcher<F2>) -> bool {
47            self.id == other.id
48        }
49    }
50
51    impl<F> Hash for DropCatcher<F>
52    where
53        F: FnOnce() + Send + 'static,
54    {
55        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
56            state.write(&self.id);
57        }
58    }
59}
60
61struct DropCatcherInner<F>
62where F: FnOnce() + Send + 'static
63{
64    f: Option<F>,
65}
66
67impl<F> Drop for DropCatcherInner<F>
68where F: FnOnce() + Send + 'static
69{
70    fn drop(&mut self) {
71        self.f.take().map(|f| {
72            f();
73        });
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use std::sync::{Arc, Mutex};
81
82    #[test]
83    fn drop_check() {
84        let flag = Arc::new(Mutex::new(false));
85        let flag_clone = flag.clone();
86        {
87            #[allow(unused_variables)]
88            let dropped = DropCatcher::new(move || {
89                *flag_clone.lock().unwrap() = true;
90            });
91        }
92
93        assert!(*flag.lock().unwrap());
94    }
95
96    #[allow(dead_code)]
97    #[cfg_attr(feature = "random-id", test)]
98    fn id_check() {
99        let flag = Arc::new(Mutex::new(false));
100        let flag_clone = flag.clone();
101        {
102            let dropped = DropCatcher::new(move || {
103                *flag_clone.lock().unwrap() = true;
104            });
105
106            let dropped_clone = dropped.clone();
107
108            assert_eq!(dropped, dropped_clone);
109
110            drop(dropped);
111            assert!(!*flag.lock().unwrap());
112        }
113
114        assert!(*flag.lock().unwrap());
115    }
116}