webserver_base/webserver/
state.rs1use std::sync::Arc;
4
5use crate::environment::Environment;
6
7use super::shutdown::Shutdown;
8
9#[cfg(feature = "templates")]
10use {
11 super::error::WebServerError,
12 crate::templates::{BaseTemplateData, PageTemplateData, TemplateData, TemplateRegistry},
13 axum::response::Html,
14 serde::Serialize,
15 std::collections::BTreeMap,
16 std::sync::LazyLock,
17};
18
19#[cfg(feature = "templates")]
21static NO_CACHE_BUSTER: LazyLock<BTreeMap<String, String>> = LazyLock::new(BTreeMap::new);
22
23#[derive(Debug)]
33pub struct WebServerState<S = ()>(Arc<Inner<S>>);
34
35impl<S> Clone for WebServerState<S> {
36 fn clone(&self) -> Self {
37 Self(Arc::clone(&self.0))
38 }
39}
40
41#[derive(Debug)]
42struct Inner<S> {
43 host: String,
44 port: u16,
45 environment: Environment,
46 shutdown: Shutdown,
47
48 #[cfg(feature = "templates")]
49 base: Option<BaseTemplateData>,
50 #[cfg(feature = "templates")]
51 templates: Option<TemplateRegistry<'static>>,
52 #[cfg(feature = "webserver")]
53 cache_buster: Option<crate::assets::CacheBuster>,
54 #[cfg(feature = "templates")]
55 frontend: Option<crate::templates::FrontendRuntime>,
56
57 app: S,
58}
59
60pub(super) struct StateParts<S> {
62 pub(super) host: String,
63 pub(super) port: u16,
64 pub(super) environment: Environment,
65 pub(super) shutdown: Shutdown,
66 #[cfg(feature = "templates")]
67 pub(super) base: Option<BaseTemplateData>,
68 #[cfg(feature = "templates")]
69 pub(super) templates: Option<TemplateRegistry<'static>>,
70 #[cfg(feature = "webserver")]
71 pub(super) cache_buster: Option<crate::assets::CacheBuster>,
72 #[cfg(feature = "templates")]
73 pub(super) frontend: Option<crate::templates::FrontendRuntime>,
74 pub(super) app: S,
75}
76
77impl<S> WebServerState<S> {
78 pub(super) fn new(parts: StateParts<S>) -> Self {
79 Self(Arc::new(Inner {
80 host: parts.host,
81 port: parts.port,
82 environment: parts.environment,
83 shutdown: parts.shutdown,
84 #[cfg(feature = "templates")]
85 base: parts.base,
86 #[cfg(feature = "templates")]
87 templates: parts.templates,
88 #[cfg(feature = "webserver")]
89 cache_buster: parts.cache_buster,
90 #[cfg(feature = "templates")]
91 frontend: parts.frontend,
92 app: parts.app,
93 }))
94 }
95
96 #[must_use]
98 pub fn host(&self) -> &str {
99 &self.0.host
100 }
101
102 #[must_use]
104 pub fn port(&self) -> u16 {
105 self.0.port
106 }
107
108 #[must_use]
110 pub fn environment(&self) -> Environment {
111 self.0.environment
112 }
113
114 #[must_use]
125 pub fn shutdown(&self) -> &Shutdown {
126 &self.0.shutdown
127 }
128
129 #[must_use]
131 pub fn app(&self) -> &S {
132 &self.0.app
133 }
134
135 #[cfg(feature = "templates")]
137 #[must_use]
138 pub fn base_template_data(&self) -> Option<&BaseTemplateData> {
139 self.0.base.as_ref()
140 }
141
142 #[cfg(feature = "templates")]
144 #[must_use]
145 pub fn templates(&self) -> Option<&TemplateRegistry<'static>> {
146 self.0.templates.as_ref()
147 }
148
149 #[cfg(feature = "webserver")]
151 #[must_use]
152 pub fn cache_buster(&self) -> Option<&crate::assets::CacheBuster> {
153 self.0.cache_buster.as_ref()
154 }
155
156 #[cfg(feature = "webserver")]
159 #[must_use]
160 pub fn asset(&self, original_asset_file_path: &str) -> String {
161 self.0.cache_buster.as_ref().map_or_else(
162 || original_asset_file_path.to_string(),
163 |cache_buster| cache_buster.get_file(original_asset_file_path),
164 )
165 }
166
167 #[cfg(feature = "templates")]
169 #[must_use]
170 pub fn frontend_runtime(&self) -> Option<&crate::templates::FrontendRuntime> {
171 self.0.frontend.as_ref()
172 }
173
174 #[cfg(feature = "templates")]
181 pub fn render<A>(
182 &self,
183 page: &PageTemplateData,
184 data: A,
185 ) -> Result<Html<String>, WebServerError>
186 where
187 A: Serialize,
188 {
189 let (Some(base), Some(registry)) = (self.0.base.as_ref(), self.0.templates.as_ref()) else {
190 return Err(WebServerError::TemplatesNotConfigured);
191 };
192
193 let template_data: TemplateData<'_, A> = TemplateData::assemble(
194 base,
195 page,
196 self.0.frontend.as_ref(),
197 self.0.environment,
198 self.cache_buster_map(),
199 data,
200 )?;
201
202 Ok(Html(registry.render(page.template(), &template_data)?))
203 }
204
205 #[cfg(feature = "templates")]
207 fn cache_buster_map(&self) -> &BTreeMap<String, String> {
208 #[cfg(feature = "webserver")]
209 {
210 self.0
211 .cache_buster
212 .as_ref()
213 .map_or(&NO_CACHE_BUSTER, crate::assets::CacheBuster::cache)
214 }
215 #[cfg(not(feature = "webserver"))]
216 {
217 &NO_CACHE_BUSTER
218 }
219 }
220}