dropcatch 0.3.0

Drop detection library for Rust.
Documentation
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());
    }
}