witchcraft_server_config/install/
mod.rs1use crate::ConfigError;
16use serde::de::Error;
17use serde::{Deserialize, Deserializer};
18use staged_builder::{staged_builder, Validate};
19use std::env;
20use std::path::{Path, PathBuf};
21use std::time::Duration;
22
23mod de;
24
25#[derive(Clone, PartialEq, Debug)]
27#[staged_builder]
28#[builder(validate)]
29pub struct InstallConfig {
30 #[builder(into)]
31 product_name: String,
32 #[builder(into)]
33 product_version: String,
34 port: u16,
35 #[builder(default, into)]
36 management_port: Option<u16>,
37 #[builder(default)]
38 keystore: KeystoreConfig,
39 #[builder(default, into)]
40 client_auth_truststore: Option<ClientAuthTruststoreConfig>,
41 #[builder(into, default = "/".to_string())]
42 context_path: String,
43 #[builder(default = env::var_os("CONTAINER").is_some())]
44 use_console_log: bool,
45 #[builder(default)]
46 server: ServerConfig,
47 #[builder(default)]
48 minidump: MinidumpConfig,
49}
50
51impl Validate for InstallConfig {
52 type Error = ConfigError;
53
54 fn validate(&self) -> Result<(), Self::Error> {
55 if !(self.context_path == "/"
56 || (self.context_path.starts_with('/') && !self.context_path.ends_with('/')))
57 {
58 return Err(ConfigError(
59 "context-path must either be `/` or start but not end with a `/`".to_string(),
60 ));
61 }
62
63 Ok(())
64 }
65}
66
67impl<'de> Deserialize<'de> for InstallConfig {
68 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69 where
70 D: Deserializer<'de>,
71 {
72 let raw = de::InstallConfig::deserialize(deserializer)?;
73 let mut builder = InstallConfig::builder()
74 .product_name(raw.product_name)
75 .product_version(raw.product_version)
76 .port(raw.port);
77 if let Some(management_port) = raw.management_port {
78 builder = builder.management_port(management_port);
79 }
80 if let Some(keystore) = raw.keystore {
81 builder = builder.keystore(keystore);
82 }
83 if let Some(client_auth_truststore) = raw.client_auth_truststore {
84 builder = builder.client_auth_truststore(client_auth_truststore);
85 }
86 if let Some(context_path) = raw.context_path {
87 builder = builder.context_path(context_path);
88 }
89 if let Some(use_console_log) = raw.use_console_log {
90 builder = builder.use_console_log(use_console_log);
91 }
92 if let Some(server) = raw.server {
93 builder = builder.server(server);
94 }
95 if let Some(minidump) = raw.minidump {
96 builder = builder.minidump(minidump);
97 }
98 builder.build().map_err(Error::custom)
99 }
100}
101
102impl AsRef<InstallConfig> for InstallConfig {
103 #[inline]
104 fn as_ref(&self) -> &InstallConfig {
105 self
106 }
107}
108
109impl InstallConfig {
110 #[inline]
114 pub fn product_name(&self) -> &str {
115 &self.product_name
116 }
117
118 #[inline]
122 pub fn product_version(&self) -> &str {
123 &self.product_version
124 }
125
126 #[inline]
130 pub fn port(&self) -> u16 {
131 self.port
132 }
133
134 pub fn management_port(&self) -> Option<u16> {
138 self.management_port
139 }
140
141 #[inline]
143 pub fn keystore(&self) -> &KeystoreConfig {
144 &self.keystore
145 }
146
147 #[inline]
155 pub fn client_auth_truststore(&self) -> Option<&ClientAuthTruststoreConfig> {
156 self.client_auth_truststore.as_ref()
157 }
158
159 #[inline]
165 pub fn context_path(&self) -> &str {
166 &self.context_path
167 }
168
169 #[inline]
173 pub fn use_console_log(&self) -> bool {
174 self.use_console_log
175 }
176
177 #[inline]
179 pub fn server(&self) -> &ServerConfig {
180 &self.server
181 }
182
183 #[inline]
185 pub fn minidump(&self) -> &MinidumpConfig {
186 &self.minidump
187 }
188}
189
190#[derive(Clone, PartialEq, Debug)]
192#[staged_builder]
193pub struct KeystoreConfig {
194 #[builder(into, default = PathBuf::from("var/security/key.pem"))]
195 key_path: PathBuf,
196 #[builder(into, default = PathBuf::from("var/security/cert.cer"))]
197 cert_path: PathBuf,
198}
199
200impl Default for KeystoreConfig {
201 #[inline]
202 fn default() -> Self {
203 KeystoreConfig::builder().build()
204 }
205}
206
207impl<'de> Deserialize<'de> for KeystoreConfig {
208 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209 where
210 D: Deserializer<'de>,
211 {
212 let raw = de::KeystoreConfig::deserialize(deserializer)?;
213 let mut builder = KeystoreConfig::builder();
214 if let Some(key_path) = raw.key_path {
215 builder = builder.key_path(key_path);
216 }
217 if let Some(cert_path) = raw.cert_path {
218 builder = builder.cert_path(cert_path);
219 }
220 Ok(builder.build())
221 }
222}
223
224impl KeystoreConfig {
225 #[inline]
229 pub fn key_path(&self) -> &Path {
230 &self.key_path
231 }
232
233 #[inline]
240 pub fn cert_path(&self) -> &Path {
241 &self.cert_path
242 }
243}
244
245#[derive(Clone, PartialEq, Debug)]
247#[staged_builder]
248pub struct ClientAuthTruststoreConfig {
249 #[builder(into, default = PathBuf::from("var/security/ca.cer"))]
250 path: PathBuf,
251}
252
253impl Default for ClientAuthTruststoreConfig {
254 #[inline]
255 fn default() -> Self {
256 ClientAuthTruststoreConfig::builder().build()
257 }
258}
259
260impl<'de> Deserialize<'de> for ClientAuthTruststoreConfig {
261 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
262 where
263 D: Deserializer<'de>,
264 {
265 let raw = de::ClientAuthTruststoreConfig::deserialize(deserializer)?;
266 let mut builder = ClientAuthTruststoreConfig::builder();
267 if let Some(path) = raw.path {
268 builder = builder.path(path);
269 }
270 Ok(builder.build())
271 }
272}
273
274impl ClientAuthTruststoreConfig {
275 #[inline]
280 pub fn path(&self) -> &Path {
281 &self.path
282 }
283}
284
285#[derive(Clone, PartialEq, Debug)]
287#[staged_builder]
288pub struct ServerConfig {
289 #[builder(default = num_cpus::get())]
290 processors: usize,
291 #[builder(default, custom(type = usize, convert = Some))]
292 min_threads: Option<usize>,
293 #[builder(default, custom(type = usize, convert = Some))]
294 max_threads: Option<usize>,
295 #[builder(default, custom(type = usize, convert = Some))]
296 max_connections: Option<usize>,
297 #[builder(default, custom(type = usize, convert = Some))]
298 io_threads: Option<usize>,
299 #[builder(default = Duration::from_secs(5 * 60))]
300 idle_thread_timeout: Duration,
301 #[builder(default = Duration::from_secs(15))]
302 shutdown_timeout: Duration,
303 #[builder(default = true)]
304 gzip: bool,
305 #[builder(default = false)]
306 http2: bool,
307 #[builder(default, into)]
308 idle_connection_timeout: Option<Duration>,
309}
310
311impl Default for ServerConfig {
312 #[inline]
313 fn default() -> Self {
314 ServerConfig::builder().build()
315 }
316}
317
318impl<'de> Deserialize<'de> for ServerConfig {
319 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
320 where
321 D: Deserializer<'de>,
322 {
323 let raw = de::ServerConfig::deserialize(deserializer)?;
324 let mut builder = ServerConfig::builder();
325 if let Some(processors) = raw.processors {
326 builder = builder.processors(processors);
327 }
328 if let Some(min_threads) = raw.min_threads {
329 builder = builder.min_threads(min_threads);
330 }
331 if let Some(max_threads) = raw.max_threads {
332 builder = builder.max_threads(max_threads);
333 }
334 if let Some(max_connections) = raw.max_connections {
335 builder = builder.max_connections(max_connections);
336 }
337 if let Some(io_threads) = raw.io_threads {
338 builder = builder.io_threads(io_threads);
339 }
340 if let Some(idle_thread_timeout) = raw.idle_thread_timeout {
341 builder = builder.idle_thread_timeout(idle_thread_timeout);
342 }
343 if let Some(shutdown_timeout) = raw.shutdown_timeout {
344 builder = builder.shutdown_timeout(shutdown_timeout);
345 }
346 if let Some(gzip) = raw.gzip {
347 builder = builder.gzip(gzip);
348 }
349 if let Some(http2) = raw.http2 {
350 builder = builder.http2(http2);
351 }
352 if let Some(idle_connection_timeout) = raw.idle_connection_timeout {
353 builder = builder.idle_connection_timeout(idle_connection_timeout);
354 }
355
356 Ok(builder.build())
357 }
358}
359
360impl ServerConfig {
361 #[inline]
367 pub fn processors(&self) -> usize {
368 self.processors
369 }
370
371 #[inline]
375 pub fn min_threads(&self) -> usize {
376 self.min_threads.unwrap_or(self.processors * 8)
377 }
378
379 #[inline]
383 pub fn max_threads(&self) -> usize {
384 self.max_threads
385 .unwrap_or_else(|| usize::max(self.processors * 32, 256))
386 }
387
388 #[inline]
392 pub fn max_connections(&self) -> usize {
393 self.max_connections
394 .unwrap_or_else(|| self.max_threads() * 10)
395 }
396
397 #[inline]
401 pub fn io_threads(&self) -> usize {
402 self.io_threads
403 .unwrap_or_else(|| usize::max(1, self.processors / 2))
404 }
405
406 #[inline]
410 pub fn idle_thread_timeout(&self) -> Duration {
411 self.idle_thread_timeout
412 }
413
414 #[inline]
418 pub fn shutdown_timeout(&self) -> Duration {
419 self.shutdown_timeout
420 }
421
422 #[inline]
426 pub fn gzip(&self) -> bool {
427 self.gzip
428 }
429
430 #[inline]
434 pub fn http2(&self) -> bool {
435 self.http2
436 }
437
438 #[inline]
443 pub fn idle_connection_timeout(&self) -> Option<Duration> {
444 self.idle_connection_timeout
445 }
446}
447
448#[derive(Clone, PartialEq, Debug)]
450#[staged_builder]
451pub struct MinidumpConfig {
452 #[builder(default = true)]
453 enabled: bool,
454 #[builder(into, default = PathBuf::from("var/data/tmp"))]
455 socket_dir: PathBuf,
456}
457
458impl Default for MinidumpConfig {
459 #[inline]
460 fn default() -> Self {
461 MinidumpConfig::builder().build()
462 }
463}
464
465impl<'de> Deserialize<'de> for MinidumpConfig {
466 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
467 where
468 D: Deserializer<'de>,
469 {
470 let raw = de::MinidumpConfig::deserialize(deserializer)?;
471 let mut builder = MinidumpConfig::builder();
472 if let Some(enabled) = raw.enabled {
473 builder = builder.enabled(enabled);
474 }
475 if let Some(socket_dir) = raw.socket_dir {
476 builder = builder.socket_dir(socket_dir);
477 }
478 Ok(builder.build())
479 }
480}
481
482impl MinidumpConfig {
483 #[inline]
489 pub fn enabled(&self) -> bool {
490 self.enabled
491 }
492
493 #[inline]
497 pub fn socket_dir(&self) -> &Path {
498 &self.socket_dir
499 }
500}