Skip to main content

camber_cli/
config.rs

1pub use camber::config::TlsConfig;
2use serde::Deserialize;
3use std::path::Path;
4
5#[derive(Debug, Deserialize)]
6pub struct Config {
7    listen: Option<Box<str>>,
8    connection_limit: Option<usize>,
9    tls: Option<TlsConfig>,
10    #[serde(rename = "site")]
11    sites: Vec<SiteConfig>,
12}
13
14#[derive(Debug, Deserialize)]
15pub struct SiteConfig {
16    host: Box<str>,
17    proxy: Option<Box<str>>,
18    root: Option<Box<str>>,
19    health_check: Option<Box<str>>,
20    health_interval: Option<u64>,
21}
22
23impl Config {
24    pub fn load(path: &Path) -> Result<Self, String> {
25        let config: Config = camber::config::load_config(path).map_err(|e| e.to_string())?;
26        config.validate()?;
27        Ok(config)
28    }
29
30    fn validate(&self) -> Result<(), String> {
31        if self.connection_limit == Some(0) {
32            return Err("connection_limit must be at least 1".to_owned());
33        }
34
35        for site in &self.sites {
36            if site.health_interval == Some(0) {
37                return Err(format!(
38                    "site \"{}\" health_interval must be at least 1",
39                    site.host
40                ));
41            }
42
43            match (&site.proxy, &site.root) {
44                (None, None) => {
45                    return Err(format!(
46                        "site \"{}\" must have at least \"proxy\" or \"root\"",
47                        site.host
48                    ));
49                }
50                _ => {}
51            }
52        }
53
54        if let Some(tls) = &self.tls {
55            tls.validate().map_err(|e| e.to_string())?;
56        }
57
58        Ok(())
59    }
60
61    pub fn listen(&self) -> &str {
62        self.listen.as_deref().unwrap_or("0.0.0.0:8080")
63    }
64
65    pub fn connection_limit(&self) -> Option<usize> {
66        self.connection_limit
67    }
68
69    pub fn tls(&self) -> Option<&TlsConfig> {
70        self.tls.as_ref()
71    }
72
73    pub fn sites(&self) -> &[SiteConfig] {
74        &self.sites
75    }
76
77    /// Collect domain names from all site host fields.
78    /// Used when auto-TLS is enabled to pass domains to the ACME provider.
79    pub fn auto_tls_domains(&self) -> Box<[&str]> {
80        self.sites
81            .iter()
82            .map(|s| s.host())
83            .collect::<Vec<_>>()
84            .into_boxed_slice()
85    }
86}
87
88impl SiteConfig {
89    pub fn host(&self) -> &str {
90        &self.host
91    }
92
93    pub fn proxy(&self) -> Option<&str> {
94        self.proxy.as_deref()
95    }
96
97    pub fn root(&self) -> Option<&str> {
98        self.root.as_deref()
99    }
100
101    pub fn health_check(&self) -> Option<&str> {
102        self.health_check.as_deref()
103    }
104
105    pub fn health_interval(&self) -> Option<u64> {
106        self.health_interval
107    }
108}