Skip to main content

sova_core/
dispatch.rs

1//! In-process dispatch handle (filled when the server starts).
2
3use crate::app::AppInner;
4use crate::handler::BoxFuture;
5use crate::request::Request;
6use crate::response::Response;
7use std::sync::{Arc, OnceLock};
8
9type DispatchFn = Arc<dyn Fn(Request) -> BoxFuture<Response> + Send + Sync>;
10
11struct Inner {
12    dispatch: DispatchFn,
13}
14
15/// Handle for dispatching requests inside the running app (DevTools console, tests).
16#[derive(Clone, Default)]
17pub struct AppDispatch {
18    slot: Arc<OnceLock<Inner>>,
19}
20
21impl AppDispatch {
22    /// Dispatch if the server has started; `None` before listen.
23    pub fn try_dispatch(&self, req: Request) -> Option<BoxFuture<Response>> {
24        Some((self.slot.get()?.dispatch)(req))
25    }
26
27    pub(crate) fn install_if_needed(&self, inner: &AppInner) {
28        if self.slot.get().is_some() {
29            return;
30        }
31        let _ = self.install(inner);
32    }
33
34    pub(crate) fn install(&self, inner: &AppInner) -> bool {
35        if self.slot.get().is_some() {
36            return false;
37        }
38        let compiled = Arc::clone(&inner.compiled);
39        let state = inner.state();
40        let dispatch: DispatchFn = Arc::new(move |mut req: Request| {
41            req.state = Arc::clone(&state);
42            let compiled = Arc::clone(&compiled);
43            Box::pin(async move { compiled.dispatch(req).await })
44        });
45        self.slot.set(Inner { dispatch }).is_ok()
46    }
47}