Skip to main content

lightshuttle_control/
state.rs

1//! Shared application state injected into axum handlers.
2//!
3//! Generic over a [`LifecycleHandle`] so the control plane stays free
4//! of runtime-specific types. axum requires the state to be `Clone`,
5//! so the handle must be cheaply cloneable.
6
7use std::sync::Arc;
8
9use lightshuttle_runtime::LifecycleHandle;
10
11use crate::metrics::Metrics;
12
13/// State shared by every route of the control plane.
14#[derive(Clone)]
15pub struct ControlState<H>
16where
17    H: LifecycleHandle + Clone,
18{
19    /// Project name as declared in the manifest.
20    pub project: String,
21    /// Lifecycle handle backing the resource endpoints.
22    pub handle: H,
23    /// Prometheus metrics renderer.
24    pub(crate) metrics: Arc<Metrics>,
25}
26
27impl<H> ControlState<H>
28where
29    H: LifecycleHandle + Clone,
30{
31    /// Build a new state bound to `project` and `handle`, with a
32    /// non-installing test metrics handle.
33    ///
34    /// The attached [`Metrics`] does not install a global recorder, so
35    /// the `metrics!` macros write nowhere and `GET /metrics` renders an
36    /// empty snapshot. This constructor is meant for tests and embedders
37    /// that do not serve metrics. In production, install the recorder
38    /// once and use [`Self::with_metrics`] instead.
39    pub fn new(project: impl Into<String>, handle: H) -> Self {
40        Self {
41            project: project.into(),
42            handle,
43            metrics: Arc::new(Metrics::for_test()),
44        }
45    }
46
47    /// Build a new state bound to `project`, `handle` and a shared
48    /// [`Metrics`] renderer (typically the globally installed one).
49    pub fn with_metrics(project: impl Into<String>, handle: H, metrics: Arc<Metrics>) -> Self {
50        Self {
51            project: project.into(),
52            handle,
53            metrics,
54        }
55    }
56}