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}