use std::{fmt::Debug, sync::Arc};
type Fn = dyn FnOnce() + Send + Sync + 'static;
#[derive(Clone)]
pub struct DropCatcher {
#[cfg(feature = "random-id")]
id: [u8; 16],
_inner: Arc<DropCatcherInner>,
}
impl DropCatcher {
pub fn new<F>(f: F) -> Self
where
F: FnOnce() + Send + Sync + 'static,
{
Self {
#[cfg(feature = "random-id")]
id: rand::random(),
_inner: Arc::new(DropCatcherInner {
f: Some(Box::new(f)),
}),
}
}
}
impl Debug for DropCatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("DropCatcher")
}
}
#[cfg(feature = "random-id")]
mod random_id_traits {
use std::hash::Hash;
use super::DropCatcher;
impl PartialEq for DropCatcher {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for DropCatcher {}
impl Hash for DropCatcher {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write(&self.id);
}
}
}
struct DropCatcherInner {
f: Option<Box<Fn>>,
}
impl Drop for DropCatcherInner {
fn drop(&mut self) {
if let Some(f) = self.f.take() {
f();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[test]
fn drop_check() {
let flag = Arc::new(Mutex::new(false));
let flag_clone = flag.clone();
{
#[allow(unused_variables)]
let dropped = DropCatcher::new(move || {
*flag_clone.lock().unwrap() = true;
});
}
assert!(*flag.lock().unwrap());
}
#[allow(dead_code)]
#[cfg_attr(feature = "random-id", test)]
fn id_check() {
let flag = Arc::new(Mutex::new(false));
let flag_clone = flag.clone();
{
let dropped = DropCatcher::new(move || {
*flag_clone.lock().unwrap() = true;
});
let dropped_clone = dropped.clone();
assert_eq!(dropped, dropped_clone);
drop(dropped);
assert!(!*flag.lock().unwrap());
}
assert!(*flag.lock().unwrap());
}
}