postgresql_commands/
createdb.rs1use crate::Settings;
2use crate::traits::CommandBuilder;
3use std::convert::AsRef;
4use std::ffi::{OsStr, OsString};
5use std::path::PathBuf;
6
7#[derive(Clone, Debug, Default)]
9pub struct CreateDbBuilder {
10    program_dir: Option<PathBuf>,
11    envs: Vec<(OsString, OsString)>,
12    tablespace: Option<OsString>,
13    echo: bool,
14    encoding: Option<OsString>,
15    locale: Option<OsString>,
16    lc_collate: Option<OsString>,
17    lc_ctype: Option<OsString>,
18    icu_locale: Option<OsString>,
19    icu_rules: Option<OsString>,
20    locale_provider: Option<OsString>,
21    owner: Option<OsString>,
22    strategy: Option<OsString>,
23    template: Option<OsString>,
24    version: bool,
25    help: bool,
26    host: Option<OsString>,
27    port: Option<u16>,
28    username: Option<OsString>,
29    no_password: bool,
30    password: bool,
31    pg_password: Option<OsString>,
32    maintenance_db: Option<OsString>,
33    dbname: Option<OsString>,
34    description: Option<OsString>,
35}
36
37impl CreateDbBuilder {
38    #[must_use]
40    pub fn new() -> Self {
41        Self::default()
42    }
43
44    pub fn from(settings: &dyn Settings) -> Self {
46        Self::new()
47            .program_dir(settings.get_binary_dir())
48            .host(settings.get_host())
49            .port(settings.get_port())
50            .username(settings.get_username())
51            .pg_password(settings.get_password())
52    }
53
54    #[must_use]
56    pub fn program_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
57        self.program_dir = Some(path.into());
58        self
59    }
60
61    #[must_use]
63    pub fn tablespace<S: AsRef<OsStr>>(mut self, tablespace: S) -> Self {
64        self.tablespace = Some(tablespace.as_ref().to_os_string());
65        self
66    }
67
68    #[must_use]
70    pub fn echo(mut self) -> Self {
71        self.echo = true;
72        self
73    }
74
75    #[must_use]
77    pub fn encoding<S: AsRef<OsStr>>(mut self, encoding: S) -> Self {
78        self.encoding = Some(encoding.as_ref().to_os_string());
79        self
80    }
81
82    #[must_use]
84    pub fn locale<S: AsRef<OsStr>>(mut self, locale: S) -> Self {
85        self.locale = Some(locale.as_ref().to_os_string());
86        self
87    }
88
89    #[must_use]
91    pub fn lc_collate<S: AsRef<OsStr>>(mut self, lc_collate: S) -> Self {
92        self.lc_collate = Some(lc_collate.as_ref().to_os_string());
93        self
94    }
95
96    #[must_use]
98    pub fn lc_ctype<S: AsRef<OsStr>>(mut self, lc_ctype: S) -> Self {
99        self.lc_ctype = Some(lc_ctype.as_ref().to_os_string());
100        self
101    }
102
103    #[must_use]
105    pub fn icu_locale<S: AsRef<OsStr>>(mut self, icu_locale: S) -> Self {
106        self.icu_locale = Some(icu_locale.as_ref().to_os_string());
107        self
108    }
109
110    #[must_use]
112    pub fn icu_rules<S: AsRef<OsStr>>(mut self, icu_rules: S) -> Self {
113        self.icu_rules = Some(icu_rules.as_ref().to_os_string());
114        self
115    }
116
117    #[must_use]
119    pub fn locale_provider<S: AsRef<OsStr>>(mut self, locale_provider: S) -> Self {
120        self.locale_provider = Some(locale_provider.as_ref().to_os_string());
121        self
122    }
123
124    #[must_use]
126    pub fn owner<S: AsRef<OsStr>>(mut self, owner: S) -> Self {
127        self.owner = Some(owner.as_ref().to_os_string());
128        self
129    }
130
131    #[must_use]
133    pub fn strategy<S: AsRef<OsStr>>(mut self, strategy: S) -> Self {
134        self.strategy = Some(strategy.as_ref().to_os_string());
135        self
136    }
137
138    #[must_use]
140    pub fn template<S: AsRef<OsStr>>(mut self, template: S) -> Self {
141        self.template = Some(template.as_ref().to_os_string());
142        self
143    }
144
145    #[must_use]
147    pub fn version(mut self) -> Self {
148        self.version = true;
149        self
150    }
151
152    #[must_use]
154    pub fn help(mut self) -> Self {
155        self.help = true;
156        self
157    }
158
159    #[must_use]
161    pub fn host<S: AsRef<OsStr>>(mut self, host: S) -> Self {
162        self.host = Some(host.as_ref().to_os_string());
163        self
164    }
165
166    #[must_use]
168    pub fn port(mut self, port: u16) -> Self {
169        self.port = Some(port);
170        self
171    }
172
173    #[must_use]
175    pub fn username<S: AsRef<OsStr>>(mut self, username: S) -> Self {
176        self.username = Some(username.as_ref().to_os_string());
177        self
178    }
179
180    #[must_use]
182    pub fn no_password(mut self) -> Self {
183        self.no_password = true;
184        self
185    }
186
187    #[must_use]
189    pub fn password(mut self) -> Self {
190        self.password = true;
191        self
192    }
193
194    #[must_use]
196    pub fn pg_password<S: AsRef<OsStr>>(mut self, pg_password: S) -> Self {
197        self.pg_password = Some(pg_password.as_ref().to_os_string());
198        self
199    }
200
201    #[must_use]
203    pub fn maintenance_db<S: AsRef<OsStr>>(mut self, db: S) -> Self {
204        self.maintenance_db = Some(db.as_ref().to_os_string());
205        self
206    }
207
208    #[must_use]
210    pub fn dbname<S: AsRef<OsStr>>(mut self, dbname: S) -> Self {
211        self.dbname = Some(dbname.as_ref().to_os_string());
212        self
213    }
214
215    #[must_use]
217    pub fn description<S: AsRef<OsStr>>(mut self, description: S) -> Self {
218        self.description = Some(description.as_ref().to_os_string());
219        self
220    }
221}
222
223impl CommandBuilder for CreateDbBuilder {
224    fn get_program(&self) -> &'static OsStr {
226        "createdb".as_ref()
227    }
228
229    fn get_program_dir(&self) -> &Option<PathBuf> {
231        &self.program_dir
232    }
233
234    fn get_args(&self) -> Vec<OsString> {
236        let mut args: Vec<OsString> = Vec::new();
237
238        if let Some(tablespace) = &self.tablespace {
239            args.push("--tablespace".into());
240            args.push(tablespace.into());
241        }
242
243        if self.echo {
244            args.push("--echo".into());
245        }
246
247        if let Some(encoding) = &self.encoding {
248            args.push("--encoding".into());
249            args.push(encoding.into());
250        }
251
252        if let Some(locale) = &self.locale {
253            args.push("--locale".into());
254            args.push(locale.into());
255        }
256
257        if let Some(lc_collate) = &self.lc_collate {
258            args.push("--lc-collate".into());
259            args.push(lc_collate.into());
260        }
261
262        if let Some(lc_ctype) = &self.lc_ctype {
263            args.push("--lc-ctype".into());
264            args.push(lc_ctype.into());
265        }
266
267        if let Some(icu_locale) = &self.icu_locale {
268            args.push("--icu-locale".into());
269            args.push(icu_locale.into());
270        }
271
272        if let Some(icu_rules) = &self.icu_rules {
273            args.push("--icu-rules".into());
274            args.push(icu_rules.into());
275        }
276
277        if let Some(locale_provider) = &self.locale_provider {
278            args.push("--locale-provider".into());
279            args.push(locale_provider.into());
280        }
281
282        if let Some(owner) = &self.owner {
283            args.push("--owner".into());
284            args.push(owner.into());
285        }
286
287        if let Some(strategy) = &self.strategy {
288            args.push("--strategy".into());
289            args.push(strategy.into());
290        }
291
292        if let Some(template) = &self.template {
293            args.push("--template".into());
294            args.push(template.into());
295        }
296
297        if self.version {
298            args.push("--version".into());
299        }
300
301        if self.help {
302            args.push("--help".into());
303        }
304
305        if let Some(host) = &self.host {
306            args.push("--host".into());
307            args.push(host.into());
308        }
309
310        if let Some(port) = &self.port {
311            args.push("--port".into());
312            args.push(port.to_string().into());
313        }
314
315        if let Some(username) = &self.username {
316            args.push("--username".into());
317            args.push(username.into());
318        }
319
320        if self.no_password {
321            args.push("--no-password".into());
322        }
323
324        if self.password {
325            args.push("--password".into());
326        }
327
328        if let Some(maintenance_db) = &self.maintenance_db {
329            args.push("--maintenance-db".into());
330            args.push(maintenance_db.into());
331        }
332
333        if let Some(dbname) = &self.dbname {
334            args.push(dbname.into());
335        }
336
337        if let Some(description) = &self.description {
338            args.push(description.into());
339        }
340
341        args
342    }
343
344    fn get_envs(&self) -> Vec<(OsString, OsString)> {
346        let mut envs: Vec<(OsString, OsString)> = self.envs.clone();
347
348        if let Some(password) = &self.pg_password {
349            envs.push(("PGPASSWORD".into(), password.into()));
350        }
351
352        envs
353    }
354
355    fn env<S: AsRef<OsStr>>(mut self, key: S, value: S) -> Self {
357        self.envs
358            .push((key.as_ref().to_os_string(), value.as_ref().to_os_string()));
359        self
360    }
361}
362
363#[cfg(test)]
364mod tests {
365    use super::*;
366    use crate::TestSettings;
367    use crate::traits::CommandToString;
368    use test_log::test;
369
370    #[test]
371    fn test_builder_new() {
372        let command = CreateDbBuilder::new().program_dir(".").build();
373        assert_eq!(
374            PathBuf::from(".").join("createdb"),
375            PathBuf::from(command.to_command_string().replace('"', ""))
376        );
377    }
378
379    #[test]
380    fn test_builder_from() {
381        let command = CreateDbBuilder::from(&TestSettings).build();
382        #[cfg(not(target_os = "windows"))]
383        let command_prefix = r#"PGPASSWORD="password" "./createdb" "#;
384        #[cfg(target_os = "windows")]
385        let command_prefix = r#"".\\createdb" "#;
386
387        assert_eq!(
388            format!(
389                r#"{command_prefix}"--host" "localhost" "--port" "5432" "--username" "postgres""#
390            ),
391            command.to_command_string()
392        );
393    }
394
395    #[test]
396    fn test_builder() {
397        let command = CreateDbBuilder::new()
398            .env("PGDATABASE", "database")
399            .tablespace("pg_default")
400            .echo()
401            .encoding("UTF8")
402            .locale("en_US.UTF-8")
403            .lc_collate("en_US.UTF-8")
404            .lc_ctype("en_US.UTF-8")
405            .icu_locale("en_US")
406            .icu_rules("standard")
407            .locale_provider("icu")
408            .owner("postgres")
409            .strategy("wal_log")
410            .template("template0")
411            .version()
412            .help()
413            .host("localhost")
414            .port(5432)
415            .username("postgres")
416            .no_password()
417            .password()
418            .pg_password("password")
419            .maintenance_db("postgres")
420            .dbname("testdb")
421            .description("Test Database")
422            .build();
423        #[cfg(not(target_os = "windows"))]
424        let command_prefix = r#"PGDATABASE="database" PGPASSWORD="password" "#;
425        #[cfg(target_os = "windows")]
426        let command_prefix = String::new();
427
428        assert_eq!(
429            format!(
430                r#"{command_prefix}"createdb" "--tablespace" "pg_default" "--echo" "--encoding" "UTF8" "--locale" "en_US.UTF-8" "--lc-collate" "en_US.UTF-8" "--lc-ctype" "en_US.UTF-8" "--icu-locale" "en_US" "--icu-rules" "standard" "--locale-provider" "icu" "--owner" "postgres" "--strategy" "wal_log" "--template" "template0" "--version" "--help" "--host" "localhost" "--port" "5432" "--username" "postgres" "--no-password" "--password" "--maintenance-db" "postgres" "testdb" "Test Database""#
431            ),
432            command.to_command_string()
433        );
434    }
435}