add plugin naming

This commit is contained in:
2026-05-27 01:39:42 -04:00
parent 1d6708e23b
commit bcb5c31c31

View File

@@ -28,8 +28,9 @@
extern crate self as axum_app_wrapper; extern crate self as axum_app_wrapper;
use std::{fmt::Display, sync::Arc}; use std::{borrow::Cow, fmt::Display, sync::Arc};
use anyhow::Context;
use axum::{Router, routing::MethodRouter}; use axum::{Router, routing::MethodRouter};
use futures::{FutureExt, future::BoxFuture}; use futures::{FutureExt, future::BoxFuture};
@@ -104,7 +105,8 @@ where
self self
} }
/// Mount routes at the given path before plugins run setup. /// Mount routes at the given path before any setup hooks run. Prefer mounting most
/// routes within plugins.
pub fn mount(mut self, path: &str, router: Router<S>) -> Self { pub fn mount(mut self, path: &str, router: Router<S>) -> Self {
self.base_router = match path { self.base_router = match path {
"" | "/" => self.base_router.merge(router), "" | "/" => self.base_router.merge(router),
@@ -113,7 +115,7 @@ where
self self
} }
/// Store a startup value for the final state conversion step. /// Store a value in router state.
pub fn store<T: Send + Sync + 'static>(mut self, state: T) -> Self { pub fn store<T: Send + Sync + 'static>(mut self, state: T) -> Self {
self.state.insert(state); self.state.insert(state);
self self
@@ -135,7 +137,10 @@ where
{ {
let mut state = self.state; let mut state = self.state;
for plugin in self.plugins.iter_mut() { for plugin in self.plugins.iter_mut() {
state = plugin.on_init(state).await?; state = plugin
.on_init(state)
.await
.with_context(|| format!("Error in on_init hook of plugin '{}'", plugin.name()))?;
} }
let state = let state =
@@ -143,18 +148,22 @@ where
let mut router = self.base_router; let mut router = self.base_router;
for plugin in self.plugins.iter_mut() { for plugin in self.plugins.iter_mut() {
router = plugin.on_setup(router, &state)?; router = plugin
.on_setup(router, &state)
.with_context(|| format!("Error in on_setup hook of plugin '{}'", plugin.name()))?;
} }
let shutdown_fns: Vec<_> = self let shutdown_fns: Vec<_> = self
.plugins .plugins
.into_iter() .into_iter()
.rev() .rev()
.filter_map(|mut p| p.on_shutdown(&state)) .filter_map(|mut p| Some((p.name(), p.on_shutdown(&state)?)))
.collect(); .collect();
let on_shutdown = async move { let on_shutdown = async move {
for shutdown_fn in shutdown_fns { for (plugin_name, shutdown_fn) in shutdown_fns {
shutdown_fn.await?; shutdown_fn.await.with_context(|| {
format!("Error in on_shutdown hook of plugin '{plugin_name}'")
})?;
} }
Ok(()) Ok(())
} }
@@ -212,6 +221,11 @@ where
/// A plugin that can participate in app initialization, router setup, and shutdown. /// A plugin that can participate in app initialization, router setup, and shutdown.
#[allow(unused_variables, reason = "trait functions with default no-op")] #[allow(unused_variables, reason = "trait functions with default no-op")]
pub trait AppPlugin<S = TypeMapState> { pub trait AppPlugin<S = TypeMapState> {
/// Plugin name
fn name(&self) -> Cow<'static, str> {
Cow::Borrowed(std::any::type_name::<Self>())
}
/// Run during startup before typed state exists. /// Run during startup before typed state exists.
/// ///
/// Use this hook to insert or transform values in the shared [`TypeMap`]. /// Use this hook to insert or transform values in the shared [`TypeMap`].
@@ -239,6 +253,7 @@ pub trait AppPlugin<S = TypeMapState> {
/// `on_init` and `on_setup` accept capturing closures. If `on_setup` uses typed state, prefer /// `on_init` and `on_setup` accept capturing closures. If `on_setup` uses typed state, prefer
/// `AdHocPlugin::<State>::new()` so the closure parameter type can be inferred. /// `AdHocPlugin::<State>::new()` so the closure parameter type can be inferred.
pub struct AdHocPlugin<S = TypeMapState> { pub struct AdHocPlugin<S = TypeMapState> {
name: Cow<'static, str>,
on_init: Option<InitFn>, on_init: Option<InitFn>,
on_setup: Option<SetupFn<S>>, on_setup: Option<SetupFn<S>>,
on_shutdown: Option<ShutdownFn<S>>, on_shutdown: Option<ShutdownFn<S>>,
@@ -249,9 +264,20 @@ type SetupFn<S> = Box<dyn FnOnce(Router<S>, &S) -> Result<Router<S>> + Send>;
type ShutdownFn<S> = Box<dyn FnOnce(&S) -> ShutdownFuture + Send>; type ShutdownFn<S> = Box<dyn FnOnce(&S) -> ShutdownFuture + Send>;
impl<S: 'static> AdHocPlugin<S> { impl<S: 'static> AdHocPlugin<S> {
/// Create an empty ad-hoc plugin. /// Create an ad-hoc plugin. Prefer `named()` to help with debugging.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
name: Cow::Borrowed("adhoc"),
on_init: None,
on_setup: None,
on_shutdown: None,
}
}
/// Create a named ad-hoc plugin.
pub fn named(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
on_init: None, on_init: None,
on_setup: None, on_setup: None,
on_shutdown: None, on_shutdown: None,
@@ -295,6 +321,10 @@ impl<S: 'static> Default for AdHocPlugin<S> {
} }
impl<S> AppPlugin<S> for AdHocPlugin<S> { impl<S> AppPlugin<S> for AdHocPlugin<S> {
fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
fn on_init(&mut self, app_state: TypeMap) -> InitFuture { fn on_init(&mut self, app_state: TypeMap) -> InitFuture {
match self.on_init.take() { match self.on_init.take() {
Some(init_fn) => async move { init_fn(app_state).await }.boxed(), Some(init_fn) => async move { init_fn(app_state).await }.boxed(),