Skip to main content

qm_server/
config.rs

1use serde::Deserialize;
2use std::sync::Arc;
3
4/// Server configuration loaded from environment variables.
5#[derive(Deserialize)]
6pub struct Config {
7    app_name: Option<Arc<str>>,
8    host: Option<Arc<str>>,
9    port: Option<u16>,
10    #[serde(skip)]
11    address: Option<Arc<str>>,
12}
13
14impl Config {
15    /// Creates a new Config from environment variables with default SERVER_ prefix.
16    pub fn new() -> envy::Result<Self> {
17        ConfigBuilder::default().build()
18    }
19
20    /// Creates a new ConfigBuilder for custom configuration.
21    pub fn builder<'a>() -> ConfigBuilder<'a> {
22        ConfigBuilder::default()
23    }
24
25    /// Returns the application name.
26    pub fn app_name(&self) -> &str {
27        self.app_name.as_deref().unwrap()
28    }
29
30    /// Returns the server address.
31    pub fn address(&self) -> &str {
32        self.address.as_deref().unwrap()
33    }
34
35    /// Returns the server port.
36    pub fn port(&self) -> u16 {
37        self.port.unwrap_or(3000)
38    }
39}
40
41/// Builder for server configuration with custom prefix support.
42#[derive(Default)]
43pub struct ConfigBuilder<'a> {
44    prefix: Option<&'a str>,
45}
46
47impl<'a> ConfigBuilder<'a> {
48    /// Sets a custom environment variable prefix.
49    pub fn with_prefix(mut self, prefix: &'a str) -> Self {
50        self.prefix = Some(prefix);
51        self
52    }
53
54    /// Builds the Config from environment variables.
55    pub fn build(self) -> envy::Result<Config> {
56        let prefix = self.prefix.unwrap_or("SERVER_");
57        let mut cfg: Config = envy::prefixed(prefix).from_env()?;
58        if cfg.app_name.is_none() {
59            cfg.app_name = Some(Arc::from("quick-microservice"));
60        }
61        let host = cfg.host.as_deref().unwrap_or("127.0.0.1");
62        let port = cfg.port.unwrap_or(3000);
63        cfg.address = Some(Arc::from(format!("{}:{}", host, port)));
64        Ok(cfg)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    #[test]
71    fn parse_builtin_config_test() -> envy::Result<()> {
72        let cfg = super::Config::builder()
73            .with_prefix("DEFAULT_SERVER_NOT_SET_IN_SHELL_")
74            .build()?;
75        assert_eq!(cfg.address(), "127.0.0.1:3000");
76        Ok(())
77    }
78
79    #[test]
80    fn parse_default_config_test() -> envy::Result<()> {
81        std::env::set_var("SERVER_HOST", "localhost");
82        std::env::set_var("SERVER_PORT", "3000");
83        let cfg = super::Config::new()?;
84        assert_eq!(cfg.address(), "localhost:3000");
85        Ok(())
86    }
87
88    #[test]
89    fn parse_prefixed_config_test() -> envy::Result<()> {
90        std::env::set_var("SERVER_CUSTOM_HOST", "localhost");
91        std::env::set_var("SERVER_CUSTOM_PORT", "3000");
92        let cfg = super::Config::builder()
93            .with_prefix("SERVER_CUSTOM_")
94            .build()?;
95        assert_eq!(cfg.address(), "localhost:3000");
96        Ok(())
97    }
98}