use crate::app::App;
use crate::app::context::AppContext;
use crate::error::RoadsterResult;
use crate::lifecycle::AppLifecycleHandler;
use crate::lifecycle::default::default_lifecycle_handlers;
use axum_core::extract::FromRef;
use itertools::Itertools;
use std::collections::BTreeMap;
use std::ops::Deref;
use thiserror::Error;
use tracing::info;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LifecycleHandlerRegistryError {
#[error("The provided `AppLifecycleHandler` was already registered: `{0}`")]
AlreadyRegistered(String),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub struct LifecycleHandlerRegistry<A, S>
where
S: Clone + Send + Sync + 'static,
AppContext: FromRef<S>,
A: App<S> + 'static,
{
state: S,
handlers: BTreeMap<String, Box<dyn AppLifecycleHandler<A, S>>>,
}
impl<A, S> LifecycleHandlerRegistry<A, S>
where
S: Clone + Send + Sync + 'static,
AppContext: FromRef<S>,
A: App<S> + 'static,
{
pub(crate) fn new(state: &S) -> Self {
Self {
state: state.clone(),
handlers: default_lifecycle_handlers(state),
}
}
pub fn register<H>(&mut self, handler: H) -> RoadsterResult<()>
where
H: AppLifecycleHandler<A, S> + 'static,
{
self.register_boxed(Box::new(handler))
}
pub(crate) fn register_boxed(
&mut self,
handler: Box<dyn AppLifecycleHandler<A, S>>,
) -> RoadsterResult<()> {
let name = handler.name();
if !handler.enabled(&self.state) {
info!(lifecycle_handler.name=%name, "Lifecycle handler is not enabled, skipping registration");
return Ok(());
}
info!(lifecycle_handler.name=%name, "Registering lifecycle handler");
if self.handlers.insert(name.clone(), handler).is_some() {
return Err(LifecycleHandlerRegistryError::AlreadyRegistered(name).into());
}
Ok(())
}
pub(crate) fn handlers(&self, state: &S) -> Vec<&dyn AppLifecycleHandler<A, S>> {
self.handlers
.values()
.sorted_by(|a, b| Ord::cmp(&a.priority(state), &b.priority(state)))
.map(|handler| handler.deref())
.collect_vec()
}
}