qm_mongodb/
config.rs

1use serde::Deserialize;
2use std::sync::Arc;
3
4#[derive(Deserialize)]
5pub struct Config {
6    host: Option<Arc<str>>,
7    port: Option<u16>,
8    username: Option<Arc<str>>,
9    password: Option<Arc<str>>,
10    database: Option<Arc<str>>,
11    root_username: Option<Arc<str>>,
12    root_password: Option<Arc<str>>,
13    root_database: Option<Arc<str>>,
14    sharded: Option<bool>,
15    #[serde(skip)]
16    address: Option<Arc<str>>,
17    #[serde(skip)]
18    root_address: Option<Arc<str>>,
19}
20
21impl Config {
22    pub fn new() -> envy::Result<Self> {
23        ConfigBuilder::default().build()
24    }
25
26    pub fn builder<'a>() -> ConfigBuilder<'a> {
27        ConfigBuilder::default()
28    }
29
30    pub fn username(&self) -> Option<&str> {
31        self.username.as_deref()
32    }
33
34    pub fn password(&self) -> Option<&str> {
35        self.password.as_deref()
36    }
37
38    pub fn address(&self) -> &str {
39        self.address.as_deref().unwrap()
40    }
41
42    pub fn root_address(&self) -> &str {
43        self.root_address.as_deref().unwrap()
44    }
45
46    pub fn sharded(&self) -> bool {
47        self.sharded.unwrap_or(false)
48    }
49
50    pub fn database(&self) -> &str {
51        self.database.as_deref().unwrap()
52    }
53
54    pub fn root_database(&self) -> &str {
55        self.root_database.as_deref().unwrap()
56    }
57}
58
59#[derive(Default)]
60pub struct ConfigBuilder<'a> {
61    prefix: Option<&'a str>,
62}
63
64impl<'a> ConfigBuilder<'a> {
65    pub fn with_prefix(mut self, prefix: &'a str) -> Self {
66        self.prefix = Some(prefix);
67        self
68    }
69
70    pub fn build(self) -> envy::Result<Config> {
71        let mut cfg: Config = if let Some(prefix) = self.prefix {
72            envy::prefixed(prefix)
73        } else {
74            envy::prefixed("MONGODB_")
75        }
76        .from_env()?;
77
78        if cfg.database.is_none() {
79            cfg.database = Some(Arc::from("test"));
80        }
81
82        if cfg.root_database.is_none() {
83            cfg.root_database = Some(Arc::from("admin"));
84        }
85
86        let database = cfg.database.as_deref().unwrap();
87        let root_database = cfg.root_database.as_deref().unwrap();
88        let host = cfg.host.as_deref().unwrap_or("127.0.0.1");
89        let port = cfg.port.unwrap_or(27017);
90        let address = match (cfg.username.as_deref(), cfg.password.as_deref()) {
91            (Some(username), Some(password)) => format!(
92                "mongodb://{}:{}@{}:{}/{}",
93                username, password, host, port, database
94            ),
95            _ => format!("mongodb://{}:{}/{}", host, port, database),
96        };
97        cfg.address = Some(Arc::from(address));
98        let root_address = match (cfg.root_username.as_deref(), cfg.root_password.as_deref()) {
99            (Some(username), Some(password)) => format!(
100                "mongodb://{}:{}@{}:{}/{}",
101                username, password, host, port, root_database
102            ),
103            _ => format!("mongodb://{}:{}/{}", host, port, root_database),
104        };
105        cfg.root_address = Some(Arc::from(root_address));
106        Ok(cfg)
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    #[test]
113    fn parse_builtin_config_test() -> envy::Result<()> {
114        let cfg = super::Config::builder()
115            .with_prefix("DEFAULT_DB_NOT_SET_IN_SHELL_")
116            .build()?;
117        assert_eq!(cfg.address(), "mongodb://127.0.0.1:27017/test");
118        assert_eq!(cfg.root_address(), "mongodb://127.0.0.1:27017/admin");
119        Ok(())
120    }
121
122    #[test]
123    fn parse_default_config_test() -> envy::Result<()> {
124        std::env::set_var("MONGODB_HOST", "localhost");
125        std::env::set_var("MONGODB_PORT", "27017");
126        std::env::set_var("MONGODB_USERNAME", "testuser");
127        std::env::set_var("MONGODB_PASSWORD", "userpw");
128        std::env::set_var("MONGODB_DATABASE", "testdb");
129        std::env::set_var("MONGODB_ROOT_USERNAME", "testadmin");
130        std::env::set_var("MONGODB_ROOT_PASSWORD", "adminpw");
131        std::env::set_var("MONGODB_ROOT_DATABASE", "admin");
132        std::env::set_var("MONGODB_SHARDED", "false");
133        let cfg = super::Config::new()?;
134        assert_eq!(
135            cfg.address(),
136            "mongodb://testuser:userpw@localhost:27017/testdb"
137        );
138        assert_eq!(
139            cfg.root_address(),
140            "mongodb://testadmin:adminpw@localhost:27017/admin"
141        );
142        Ok(())
143    }
144
145    #[test]
146    fn parse_prefixed_config_test() -> envy::Result<()> {
147        std::env::set_var("MGMTDB_HOST", "localhost");
148        std::env::set_var("MGMTDB_PORT", "27017");
149        std::env::set_var("MGMTDB_USERNAME", "testuser");
150        std::env::set_var("MGMTDB_PASSWORD", "userpw");
151        std::env::set_var("MGMTDB_DATABASE", "testdb");
152        std::env::set_var("MGMTDB_ROOT_USERNAME", "testadmin");
153        std::env::set_var("MGMTDB_ROOT_PASSWORD", "adminpw");
154        std::env::set_var("MGMTDB_ROOT_DATABASE", "admin");
155        std::env::set_var("MGMTDB_SHARDED", "false");
156        let cfg = super::Config::builder().with_prefix("MGMTDB_").build()?;
157        assert_eq!(
158            cfg.address(),
159            "mongodb://testuser:userpw@localhost:27017/testdb"
160        );
161        assert_eq!(
162            cfg.root_address(),
163            "mongodb://testadmin:adminpw@localhost:27017/admin"
164        );
165        Ok(())
166    }
167}