use std::sync::Arc;
use std::time::Duration;
use crate::error::BreakerState;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
AttemptFailed { attempt: u32 },
AttemptTimedOut { attempt: u32 },
RetryScheduled { attempt: u32, delay: Duration },
Succeeded { attempts: u32 },
GaveUp { attempts: u32 },
CircuitStateChanged { to: BreakerState },
ConcurrencyRejected,
RetryBudgetExhausted { attempts: u32 },
}
pub(crate) type EventHook = Arc<dyn Fn(&Event) + Send + Sync>;
#[inline]
pub(crate) fn emit(hook: &Option<EventHook>, make: impl FnOnce() -> Event) {
if let Some(h) = hook {
h(&make());
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
#[test]
fn emit_skips_when_no_hook() {
let none: Option<EventHook> = None;
let called = std::cell::Cell::new(false);
emit(&none, || {
called.set(true);
Event::Succeeded { attempts: 1 }
});
assert!(
!called.get(),
"event must not be constructed without a hook"
);
}
#[test]
fn emit_invokes_hook() {
let seen: Arc<Mutex<Vec<Event>>> = Arc::new(Mutex::new(Vec::new()));
let sink = Arc::clone(&seen);
let hook: Option<EventHook> = Some(Arc::new(move |e: &Event| {
sink.lock().unwrap().push(e.clone())
}));
emit(&hook, || Event::Succeeded { attempts: 3 });
assert_eq!(
seen.lock().unwrap().as_slice(),
&[Event::Succeeded { attempts: 3 }]
);
}
}