From 65007f6cd249d2f7bcb741431f925a30def0efca Mon Sep 17 00:00:00 2001 From: fa-sharp Date: Tue, 26 May 2026 01:47:53 -0400 Subject: [PATCH] AdHocPlugin on_init and on_setup accept capturing closures --- src/lib.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8af1288..8e83cf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ pub trait AppPlugin> { /// Utility to build a plugin on the fly pub struct AdHocPlugin> { on_init: Option BoxFuture<'static, anyhow::Result> + Send>>, - on_setup: Option, state: &S) -> anyhow::Result>>, + on_setup: Option, &S) -> anyhow::Result> + Send>>, on_shutdown: Option BoxFuture<'static, ()>>>, } @@ -133,8 +133,9 @@ impl AdHocPlugin { } /// Init function that will run on server startup. Can add and manipulate state. - pub fn on_init(mut self, on_init: fn(app_state: TypeMap) -> T) -> Self + pub fn on_init(mut self, on_init: F) -> Self where + F: FnOnce(TypeMap) -> T + Send + 'static, T: Future> + Send + 'static, { self.on_init = Some(Box::new(move |s| Box::pin(on_init(s)))); @@ -143,11 +144,11 @@ impl AdHocPlugin { /// Setup function that will run _after_ state is initialized. (e.g. routes, middleware, /// and services should be added here) - pub fn on_setup( - mut self, - on_setup: fn(router: Router, state: &S) -> anyhow::Result>, - ) -> Self { - self.on_setup = Some(on_setup); + pub fn on_setup(mut self, on_setup: F) -> Self + where + F: FnOnce(Router, &S) -> anyhow::Result> + Send + 'static, + { + self.on_setup = Some(Box::new(on_setup)); self } @@ -170,7 +171,7 @@ impl AppPlugin for AdHocPlugin { } fn on_setup(&mut self, router: Router, state: &S) -> anyhow::Result> { - match self.on_setup { + match self.on_setup.take() { Some(setup_fn) => setup_fn(router, state), None => Ok(router), } @@ -183,3 +184,47 @@ impl AppPlugin for AdHocPlugin { } } } + +#[cfg(test)] +mod tests { + use std::convert::Infallible; + + use super::*; + + #[derive(Clone)] + struct TestState { + value: Arc, + } + + impl TryFrom for TestState { + type Error = Infallible; + + fn try_from(mut map: TypeMap) -> Result { + Ok(Self { + value: map.remove::>().expect("missing state value"), + }) + } + } + + #[test] + fn adhoc_plugin_with_basic_state() { + let init_value = Arc::new(String::from("ready")); + let setup_value = Arc::clone(&init_value); + + let plugin = AdHocPlugin::::new() + .on_init(async |mut state| { + state.insert(init_value); + Ok(state) + }) + .on_setup(move |router, state| { + assert_eq!(state.value.as_str(), setup_value.as_str()); + Ok(router) + }); + let app = App::::new().register(plugin); + + let (_router, state, _shutdown) = + futures::executor::block_on(app.init()).expect("app should initialize"); + + assert_eq!(state.value.as_str(), "ready"); + } +}