use std::{fmt::Debug, sync::Arc};
#[derive(Clone)]
pub struct DropCatcher<F>
where F: FnOnce() + Send + 'static
{
#[cfg(feature = "random-id")]
id: [u8; 16],
_inner: Arc<DropCatcherInner<F>>
}
impl<F> DropCatcher<F>
where F: FnOnce() + Send + 'static
{
pub fn new(f: F) -> Self {
Self {
#[cfg(feature = "random-id")]
id: rand::random(),
_inner: Arc::new(DropCatcherInner { f: Some(f) }),
}
}
}
impl<F> Debug for DropCatcher<F>
where
F: FnOnce() + Send + 'static
{
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<F1, F2> PartialEq<DropCatcher<F2>> for DropCatcher<F1>
where
F1: FnOnce() + Send + 'static,
F2: FnOnce() + Send + 'static,
{
fn eq(&self, other: &DropCatcher<F2>) -> bool {
self.id == other.id
}
}
impl<F> Hash for DropCatcher<F>
where
F: FnOnce() + Send + 'static,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write(&self.id);
}
}
}
struct DropCatcherInner<F>
where F: FnOnce() + Send + 'static
{
f: Option<F>,
}
impl<F> Drop for DropCatcherInner<F>
where F: FnOnce() + Send + 'static
{
fn drop(&mut self) {
self.f.take().map(|f| {
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());
}
}