use std::fmt;
use std::sync::Arc;
pub struct Hook<A>(Option<Callback<A>>);
type Callback<A> = Arc<dyn Fn(&A) + Send + Sync>;
impl<A> Hook<A> {
pub const fn none() -> Self {
Self(None)
}
pub fn set(&mut self, f: Option<impl Fn(&A) + Send + Sync + 'static>) {
self.0 = f.map(|f| Arc::new(f) as Callback<A>);
}
pub fn is_set(&self) -> bool {
self.0.is_some()
}
pub fn fire(&self, arg: &A) {
if let Some(f) = &self.0 {
f(arg);
}
}
}
impl<A> Default for Hook<A> {
fn default() -> Self {
Self::none()
}
}
impl<A> Clone for Hook<A> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A> fmt::Debug for Hook<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.0.is_some() {
"Hook(set)"
} else {
"Hook(none)"
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SwitchFailure {
pub name: String,
pub message: String,
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
#[test]
fn hook_fires_until_removed() {
let n = Arc::new(AtomicU32::new(0));
let mut h = Hook::<u32>::none();
h.fire(&1);
let n2 = n.clone();
h.set(Some(move |v: &u32| {
n2.fetch_add(*v, Ordering::SeqCst);
}));
let copy = h.clone();
h.fire(&2);
copy.fire(&3);
assert_eq!(n.load(Ordering::SeqCst), 5);
h.set(None::<fn(&u32)>);
h.fire(&10);
assert_eq!(n.load(Ordering::SeqCst), 5);
}
}