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}