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}