Skip to main content

sofie_http/
app.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use sofie_core::Result;
5use sofie_db::Db;
6use tokio::net::TcpListener;
7
8#[cfg(feature = "engine")]
9use crate::Screens;
10use crate::admission::AreaPolicy;
11use crate::routes::Route;
12use crate::throttle::{Limiter, Rate};
13use crate::{Area, AreaMembers, RouteName, Routes, Serving, server};
14
15pub struct App {
16    pub(crate) db: Db,
17    #[cfg(feature = "engine")]
18    pub(crate) screens: Screens,
19    pub(crate) routes: Vec<Route>,
20    #[cfg(feature = "islands")]
21    pub(crate) islands: Vec<crate::islands::IslandScreen>,
22    pub(crate) names: Vec<RouteName>,
23    pub(crate) areas: HashMap<&'static str, AreaPolicy>,
24    pub(crate) members: Option<Arc<dyn AreaMembers>>,
25    #[cfg(feature = "gates")]
26    pub(crate) gates: crate::Gates,
27    #[cfg(feature = "steps")]
28    pub(crate) steps: Vec<Box<dyn crate::PipelineStep>>,
29    #[cfg(feature = "cors")]
30    pub(crate) cors: HashMap<&'static str, crate::Cors>,
31    pub(crate) serving: Serving,
32}
33
34impl App {
35    #[must_use]
36    pub fn new(db: Db) -> Self {
37        Self {
38            db,
39            #[cfg(feature = "engine")]
40            screens: Screens::default(),
41            routes: Vec::new(),
42            #[cfg(feature = "islands")]
43            islands: Vec::new(),
44            names: Vec::new(),
45            areas: HashMap::new(),
46            members: None,
47            #[cfg(feature = "gates")]
48            gates: crate::Gates::default(),
49            #[cfg(feature = "steps")]
50            steps: Vec::new(),
51            #[cfg(feature = "cors")]
52            cors: HashMap::new(),
53            serving: Serving::default(),
54        }
55    }
56
57    #[cfg(feature = "steps")]
58    #[must_use]
59    pub fn step<A: Area>(mut self, step: impl crate::PipelineStep) -> Self {
60        self.areas
61            .entry(A::NAME)
62            .or_default()
63            .steps
64            .push(Box::new(step));
65        self
66    }
67
68    /// Runs on every routed request before the area's steps, and on every answer, a 404 too.
69    #[cfg(feature = "steps")]
70    #[must_use]
71    pub fn step_all(mut self, step: impl crate::PipelineStep) -> Self {
72        self.steps.push(Box::new(step));
73        self
74    }
75
76    /// Replaces the area's earlier policy.
77    #[cfg(feature = "cors")]
78    #[must_use]
79    pub fn cors<A: Area>(mut self, cors: crate::Cors) -> Self {
80        self.cors.insert(A::NAME, cors);
81        self
82    }
83
84    /// Keeps the menus given so far, which live beside the gates.
85    #[cfg(feature = "gates")]
86    #[must_use]
87    pub fn gates(mut self, gates: crate::Gates) -> Self {
88        #[cfg(feature = "menu")]
89        let menus = std::mem::take(&mut self.gates.menus);
90        self.gates = gates;
91        #[cfg(feature = "menu")]
92        {
93            self.gates.menus = menus;
94        }
95        self
96    }
97
98    /// Besides any route's own limit.
99    #[must_use]
100    pub fn throttle<A: Area>(mut self, rate: Rate) -> Self {
101        self.areas.entry(A::NAME).or_default().throttle = Some(Limiter::new(rate));
102        self
103    }
104
105    /// Where grants, bearer tokens and lock notices are read.
106    #[must_use]
107    pub fn membership(mut self, members: impl AreaMembers) -> Self {
108        self.members = Some(Arc::new(members));
109        self
110    }
111
112    /// Changes what was set before, so the process and a test server each add their own.
113    #[must_use]
114    pub fn serving(mut self, configure: impl FnOnce(Serving) -> Serving) -> Self {
115        self.serving = configure(std::mem::take(&mut self.serving));
116        self
117    }
118
119    #[cfg(feature = "engine")]
120    #[must_use]
121    pub fn screens(mut self, screens: Screens) -> Self {
122        self.screens = screens;
123        self
124    }
125
126    #[must_use]
127    pub fn area<A: Area>(
128        mut self,
129        module: &'static str,
130        define: impl FnOnce(&mut Routes<A>),
131    ) -> Self {
132        let mut routes = Routes::new(module);
133        define(&mut routes);
134        self.routes.extend(routes.added);
135        #[cfg(feature = "islands")]
136        self.islands.extend(routes.islands);
137        self
138    }
139
140    /// Finishes open requests after `shutdown` completes.
141    pub async fn serve(
142        self,
143        listener: TcpListener,
144        shutdown: impl Future<Output = ()>,
145    ) -> Result<()> {
146        server::serve(self, listener, shutdown).await
147    }
148}