Skip to main content

boson_axum/
state.rs

1//! Shared Axum state for Boson handlers.
2
3use std::sync::Arc;
4
5use boson_runtime::Boson;
6use serde_json::Value as JsonValue;
7
8use crate::auth::{require_admin_auth_from_env, AdminAuth};
9use crate::error::BosonAxumError;
10
11/// Callback that supplies `actor_json` for HTTP enqueue (overrides the default service marker).
12pub type HttpEnqueueActorProvider = Arc<dyn Fn() -> JsonValue + Send + Sync>;
13
14/// Extractable state holding a [`Boson`] runtime and optional admin auth.
15///
16/// Construct with [`BosonState::new`] or [`BosonState::builder`].
17#[derive(Clone)]
18pub struct BosonState {
19    /// Boson runtime for admin and enqueue operations.
20    pub boson: Arc<Boson>,
21    /// Optional host verifier for admin routes.
22    pub admin_auth: Option<Arc<dyn AdminAuth>>,
23    /// When true, requests are rejected if [`admin_auth`](Self::admin_auth) is `None`.
24    pub require_admin_auth: bool,
25    /// Optional override for HTTP enqueue actor JSON.
26    pub http_enqueue_actor: Option<HttpEnqueueActorProvider>,
27}
28
29impl BosonState {
30    /// Create state from a shared Boson instance (no admin auth; require-flag from env).
31    ///
32    /// ```rust,no_run
33    /// use std::sync::Arc;
34    /// use boson_axum::BosonState;
35    /// use boson_runtime::Boson;
36    ///
37    /// # fn demo(boson: Boson) {
38    /// let state = BosonState::new(Arc::new(boson));
39    /// # let _ = state;
40    /// # }
41    /// ```
42    #[must_use]
43    pub fn new(boson: Arc<Boson>) -> Self {
44        Self {
45            boson,
46            admin_auth: None,
47            require_admin_auth: require_admin_auth_from_env(),
48            http_enqueue_actor: None,
49        }
50    }
51
52    /// Builder for authenticated / customized admin mounts.
53    #[must_use]
54    pub fn builder(boson: Arc<Boson>) -> BosonStateBuilder {
55        BosonStateBuilder {
56            boson,
57            admin_auth: None,
58            require_admin_auth: require_admin_auth_from_env(),
59            http_enqueue_actor: None,
60        }
61    }
62
63    /// Actor JSON used for `POST /jobs/enqueue`.
64    #[must_use]
65    pub fn enqueue_actor_json(&self) -> JsonValue {
66        if let Some(ref provider) = self.http_enqueue_actor {
67            return provider();
68        }
69        boson_core::default_http_enqueue_actor()
70    }
71}
72
73/// Build [`BosonState`] with admin auth and actor overrides.
74pub struct BosonStateBuilder {
75    boson: Arc<Boson>,
76    admin_auth: Option<Arc<dyn AdminAuth>>,
77    require_admin_auth: bool,
78    http_enqueue_actor: Option<HttpEnqueueActorProvider>,
79}
80
81impl BosonStateBuilder {
82    /// Install a host [`AdminAuth`] verifier.
83    #[must_use]
84    pub fn admin_auth(mut self, auth: Arc<dyn AdminAuth>) -> Self {
85        self.admin_auth = Some(auth);
86        self
87    }
88
89    /// Force require-admin-auth (overrides env when set).
90    #[must_use]
91    pub const fn require_admin_auth(mut self, require: bool) -> Self {
92        self.require_admin_auth = require;
93        self
94    }
95
96    /// Override HTTP enqueue actor JSON.
97    #[must_use]
98    pub fn http_enqueue_actor(
99        mut self,
100        provider: impl Fn() -> JsonValue + Send + Sync + 'static,
101    ) -> Self {
102        self.http_enqueue_actor = Some(Arc::new(provider));
103        self
104    }
105
106    /// Build state.
107    ///
108    /// # Errors
109    ///
110    /// Returns [`BosonAxumError::MissingAdminAuth`] when `require_admin_auth` is set and no
111    /// verifier was installed.
112    pub fn build(self) -> Result<BosonState, BosonAxumError> {
113        if self.require_admin_auth && self.admin_auth.is_none() {
114            return Err(BosonAxumError::MissingAdminAuth);
115        }
116        Ok(BosonState {
117            boson: self.boson,
118            admin_auth: self.admin_auth,
119            require_admin_auth: self.require_admin_auth,
120            http_enqueue_actor: self.http_enqueue_actor,
121        })
122    }
123}