Skip to main content

jokoway_core/config/
builder.rs

1use super::models::{JokowayConfig, RootConfig, ServerConf, Service, TlsSettings, Upstream};
2use serde_yaml;
3use std::fs::File;
4use std::io::BufReader;
5use std::path::Path;
6
7pub struct ConfigBuilder {
8    config: JokowayConfig,
9    server_conf: Option<ServerConf>,
10}
11
12impl Default for ConfigBuilder {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl ConfigBuilder {
19    pub fn new() -> Self {
20        ConfigBuilder {
21            config: JokowayConfig::default(),
22            server_conf: Some(ServerConf::default()),
23        }
24    }
25
26    /// Load configuration from a YAML file with optimized error handling.
27    /// This will merge or overwrite existing configuration depending on implementation.
28    /// Here we assume it loads the base config.
29    pub fn from_file<P: AsRef<Path>>(
30        mut self,
31        path: P,
32    ) -> Result<Self, Box<dyn std::error::Error>> {
33        let path_ref = path.as_ref();
34
35        // Pre-validate file exists for better error messages
36        if !path_ref.exists() {
37            return Err(format!("Configuration file not found: {}", path_ref.display()).into());
38        }
39
40        let file = File::open(path_ref)?;
41        let reader = BufReader::new(file);
42
43        // Parse with better error context
44        let root: RootConfig = serde_yaml::from_reader(reader).map_err(|e| {
45            format!(
46                "Failed to parse YAML config from {}: {}",
47                path_ref.display(),
48                e
49            )
50        })?;
51
52        self.config = root.jokoway;
53        if root.pingora.is_some() {
54            self.server_conf = root.pingora;
55        }
56        Ok(self)
57    }
58
59    pub fn with_tls(mut self, tls: TlsSettings) -> Self {
60        self.config.tls = Some(tls);
61        self
62    }
63
64    pub fn add_upstream(mut self, upstream: Upstream) -> Self {
65        self.config.upstreams.push(upstream);
66        self
67    }
68
69    pub fn add_service(mut self, service: Service) -> Self {
70        self.config.services.push(std::sync::Arc::new(service));
71        self
72    }
73
74    /// A generic extension point.
75    /// Users can pass a closure to modify the internal config directly.
76    /// This allows for arbitrary modifications without changing the Builder struct.
77    ///
78    /// Example:
79    /// ```rust
80    /// use jokoway_core::config::ConfigBuilder;
81    ///
82    /// let builder = ConfigBuilder::new();
83    /// builder.configure(|cfg, _server_conf| {
84    ///     cfg.http_listen = "0.0.0.0:8080".to_string();
85    /// });
86    /// ```
87    pub fn configure<F>(mut self, func: F) -> Self
88    where
89        F: FnOnce(&mut JokowayConfig, &mut ServerConf),
90    {
91        if let Some(ref mut sc) = self.server_conf {
92            func(&mut self.config, sc);
93        }
94        self
95    }
96
97    pub fn build(self) -> (JokowayConfig, Option<ServerConf>) {
98        (self.config, self.server_conf)
99    }
100}