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