pepe_config/
postgres.rs

1use std::time::Duration;
2
3use duration_string::DurationString;
4use secrecy::{ExposeSecret, SecretString};
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
8#[serde(deny_unknown_fields)]
9pub struct Config {
10    pub user: String,
11    pub dbname: String,
12    pub host: String,
13    pub port: u16,
14    #[serde(skip_serializing)]
15    pub password: Option<SecretString>,
16    pub connect_timeout: Option<DurationString>,
17    pub options: Option<String>,
18}
19
20impl Config {
21    pub fn connection_string(&self) -> SecretString {
22        SecretString::from(format!(
23            "user={user} dbname={dbname} host={host} port={port}{password}{timeout}{options}",
24            user = self.user,
25            dbname = self.dbname,
26            host = self.host,
27            port = self.port,
28            password = match &self.password {
29                Some(p) => format!(" password={}", p.expose_secret()),
30                None => "".to_string(),
31            },
32            timeout = match self.connect_timeout {
33                Some(c) => {
34                    let d: Duration = c.into();
35                    format!(" connect_timeout={}", d.as_secs())
36                }
37                None => "".to_string(),
38            },
39            options = match &self.options {
40                Some(o) => format!(" {}", o),
41                None => "".to_string(),
42            },
43        ))
44    }
45
46    pub fn connection_url(&self) -> SecretString {
47        SecretString::from(format!(
48            "postgresql://{user}{password}@{host}:{port}/{dbname}{timeout}{options}",
49            user = self.user,
50            password = match &self.password {
51                Some(p) => format!(":{}", p.expose_secret()),
52                None => "".to_string(),
53            },
54            host = self.host,
55            port = self.port,
56            dbname = self.dbname,
57            timeout = match self.connect_timeout {
58                Some(c) => {
59                    let d: Duration = c.into();
60                    format!("?connect_timeout={}", d.as_secs())
61                }
62                None => "".to_string(),
63            },
64            options = match &self.options {
65                Some(o) => {
66                    format!(
67                        "{}{}",
68                        match self.connect_timeout {
69                            Some(_) => "&".to_string(),
70                            None => "?".to_string(),
71                        },
72                        o
73                    )
74                }
75                None => "".to_string(),
76            }
77        ))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use duration_string::DurationString;
84    use secrecy::ExposeSecret;
85
86    use crate::postgres::Config;
87
88    #[test]
89    fn connection_string() {
90        [
91            (
92                Config { ..init_config() },
93                "user=user dbname=dbname host=host port=1111 password=password connect_timeout=1 option=option".to_string(),
94            ),
95            (
96                Config {
97                    password: None,
98                    ..init_config()
99                },
100                "user=user dbname=dbname host=host port=1111 connect_timeout=1 option=option".to_string(),
101            ),
102            (
103                Config {
104                    connect_timeout: None,
105                    ..init_config()
106                },
107                "user=user dbname=dbname host=host port=1111 password=password option=option".to_string(),
108            ),
109            (
110                Config {
111                    options: None,
112                    ..init_config()
113                },
114                "user=user dbname=dbname host=host port=1111 password=password connect_timeout=1".to_string(),
115            ),
116        ]
117            .iter()
118            .for_each(|(config, expected)| assert_eq!(expected, config.connection_string().expose_secret()))
119    }
120
121    #[test]
122    fn connection_url_test() {
123        [
124            (
125                Config { ..init_config() },
126                "postgresql://user:password@host:1111/dbname?connect_timeout=1&option=option"
127                    .to_string(),
128            ),
129            (
130                Config {
131                    password: None,
132                    ..init_config()
133                },
134                "postgresql://user@host:1111/dbname?connect_timeout=1&option=option".to_string(),
135            ),
136            (
137                Config {
138                    connect_timeout: None,
139                    ..init_config()
140                },
141                "postgresql://user:password@host:1111/dbname?option=option".to_string(),
142            ),
143            (
144                Config {
145                    options: None,
146                    ..init_config()
147                },
148                "postgresql://user:password@host:1111/dbname?connect_timeout=1".to_string(),
149            ),
150        ]
151        .iter()
152        .for_each(|(conf, expected)| assert_eq!(expected, conf.connection_url().expose_secret()))
153    }
154
155    fn init_config() -> Config {
156        Config {
157            user: "user".to_string(),
158            dbname: "dbname".to_string(),
159            host: "host".to_string(),
160            port: 1111,
161            password: Some("password".to_string().into()),
162            connect_timeout: Some(DurationString::from_string("1s".to_string()).unwrap()),
163            options: Some("option=option".to_string()),
164        }
165    }
166}