use std::pin::Pin;
use std::sync::atomic::{AtomicU64, Ordering};
pub type HookFuture = Pin<Box<dyn std::future::Future<Output = ()> + Send>>;
pub type Phase = &'static str;
#[derive(Debug)]
pub struct LifecycleHookRegistration {
pub phase: Phase,
pub create: fn() -> HookFuture,
}
impl LifecycleHookRegistration {
pub const fn new(phase: Phase, create: fn() -> HookFuture) -> Self {
Self { phase, create }
}
}
inventory::collect!(LifecycleHookRegistration);
static STARTED: AtomicU64 = AtomicU64::new(0);
static STOPPED: AtomicU64 = AtomicU64::new(0);
pub async fn run_on_start() {
for hook in inventory::iter::<LifecycleHookRegistration>() {
if hook.phase == "start" {
STARTED.fetch_add(1, Ordering::Relaxed);
let fut = (hook.create)();
let _ = std::panic::AssertUnwindSafe(fut).catch_unwind().await;
}
}
}
pub async fn run_on_stop() {
for hook in inventory::iter::<LifecycleHookRegistration>() {
if hook.phase == "stop" {
STOPPED.fetch_add(1, Ordering::Relaxed);
let fut = (hook.create)();
let _ = std::panic::AssertUnwindSafe(fut).catch_unwind().await;
}
}
}
pub fn started_count() -> u64 {
STARTED.load(Ordering::Relaxed)
}
pub fn stopped_count() -> u64 {
STOPPED.load(Ordering::Relaxed)
}
use futures_util::FutureExt as _;
#[cfg(all(test, feature = "lifecycle"))]
mod tests {
use super::*;
#[test]
fn registration_is_const_constructible() {
fn runner() -> HookFuture {
Box::pin(async {})
}
let reg = LifecycleHookRegistration::new("start", runner);
assert_eq!(reg.phase, "start");
}
}