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 let mut builder = 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 if let Some(socket_dir) = settings.get_socket_dir() {
53 builder = builder.host(socket_dir.to_string_lossy().to_string());
54 }
55 builder
56 }
57
58 #[must_use]
60 pub fn program_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
61 self.program_dir = Some(path.into());
62 self
63 }
64
65 #[must_use]
67 pub fn tablespace<S: AsRef<OsStr>>(mut self, tablespace: S) -> Self {
68 self.tablespace = Some(tablespace.as_ref().to_os_string());
69 self
70 }
71
72 #[must_use]
74 pub fn echo(mut self) -> Self {
75 self.echo = true;
76 self
77 }
78
79 #[must_use]
81 pub fn encoding<S: AsRef<OsStr>>(mut self, encoding: S) -> Self {
82 self.encoding = Some(encoding.as_ref().to_os_string());
83 self
84 }
85
86 #[must_use]
88 pub fn locale<S: AsRef<OsStr>>(mut self, locale: S) -> Self {
89 self.locale = Some(locale.as_ref().to_os_string());
90 self
91 }
92
93 #[must_use]
95 pub fn lc_collate<S: AsRef<OsStr>>(mut self, lc_collate: S) -> Self {
96 self.lc_collate = Some(lc_collate.as_ref().to_os_string());
97 self
98 }
99
100 #[must_use]
102 pub fn lc_ctype<S: AsRef<OsStr>>(mut self, lc_ctype: S) -> Self {
103 self.lc_ctype = Some(lc_ctype.as_ref().to_os_string());
104 self
105 }
106
107 #[must_use]
109 pub fn icu_locale<S: AsRef<OsStr>>(mut self, icu_locale: S) -> Self {
110 self.icu_locale = Some(icu_locale.as_ref().to_os_string());
111 self
112 }
113
114 #[must_use]
116 pub fn icu_rules<S: AsRef<OsStr>>(mut self, icu_rules: S) -> Self {
117 self.icu_rules = Some(icu_rules.as_ref().to_os_string());
118 self
119 }
120
121 #[must_use]
123 pub fn locale_provider<S: AsRef<OsStr>>(mut self, locale_provider: S) -> Self {
124 self.locale_provider = Some(locale_provider.as_ref().to_os_string());
125 self
126 }
127
128 #[must_use]
130 pub fn owner<S: AsRef<OsStr>>(mut self, owner: S) -> Self {
131 self.owner = Some(owner.as_ref().to_os_string());
132 self
133 }
134
135 #[must_use]
137 pub fn strategy<S: AsRef<OsStr>>(mut self, strategy: S) -> Self {
138 self.strategy = Some(strategy.as_ref().to_os_string());
139 self
140 }
141
142 #[must_use]
144 pub fn template<S: AsRef<OsStr>>(mut self, template: S) -> Self {
145 self.template = Some(template.as_ref().to_os_string());
146 self
147 }
148
149 #[must_use]
151 pub fn version(mut self) -> Self {
152 self.version = true;
153 self
154 }
155
156 #[must_use]
158 pub fn help(mut self) -> Self {
159 self.help = true;
160 self
161 }
162
163 #[must_use]
165 pub fn host<S: AsRef<OsStr>>(mut self, host: S) -> Self {
166 self.host = Some(host.as_ref().to_os_string());
167 self
168 }
169
170 #[must_use]
172 pub fn port(mut self, port: u16) -> Self {
173 self.port = Some(port);
174 self
175 }
176
177 #[must_use]
179 pub fn username<S: AsRef<OsStr>>(mut self, username: S) -> Self {
180 self.username = Some(username.as_ref().to_os_string());
181 self
182 }
183
184 #[must_use]
186 pub fn no_password(mut self) -> Self {
187 self.no_password = true;
188 self
189 }
190
191 #[must_use]
193 pub fn password(mut self) -> Self {
194 self.password = true;
195 self
196 }
197
198 #[must_use]
200 pub fn pg_password<S: AsRef<OsStr>>(mut self, pg_password: S) -> Self {
201 self.pg_password = Some(pg_password.as_ref().to_os_string());
202 self
203 }
204
205 #[must_use]
207 pub fn maintenance_db<S: AsRef<OsStr>>(mut self, db: S) -> Self {
208 self.maintenance_db = Some(db.as_ref().to_os_string());
209 self
210 }
211
212 #[must_use]
214 pub fn dbname<S: AsRef<OsStr>>(mut self, dbname: S) -> Self {
215 self.dbname = Some(dbname.as_ref().to_os_string());
216 self
217 }
218
219 #[must_use]
221 pub fn description<S: AsRef<OsStr>>(mut self, description: S) -> Self {
222 self.description = Some(description.as_ref().to_os_string());
223 self
224 }
225}
226
227impl CommandBuilder for CreateDbBuilder {
228 fn get_program(&self) -> &'static OsStr {
230 "createdb".as_ref()
231 }
232
233 fn get_program_dir(&self) -> &Option<PathBuf> {
235 &self.program_dir
236 }
237
238 fn get_args(&self) -> Vec<OsString> {
240 let mut args: Vec<OsString> = Vec::new();
241
242 if let Some(tablespace) = &self.tablespace {
243 args.push("--tablespace".into());
244 args.push(tablespace.into());
245 }
246
247 if self.echo {
248 args.push("--echo".into());
249 }
250
251 if let Some(encoding) = &self.encoding {
252 args.push("--encoding".into());
253 args.push(encoding.into());
254 }
255
256 if let Some(locale) = &self.locale {
257 args.push("--locale".into());
258 args.push(locale.into());
259 }
260
261 if let Some(lc_collate) = &self.lc_collate {
262 args.push("--lc-collate".into());
263 args.push(lc_collate.into());
264 }
265
266 if let Some(lc_ctype) = &self.lc_ctype {
267 args.push("--lc-ctype".into());
268 args.push(lc_ctype.into());
269 }
270
271 if let Some(icu_locale) = &self.icu_locale {
272 args.push("--icu-locale".into());
273 args.push(icu_locale.into());
274 }
275
276 if let Some(icu_rules) = &self.icu_rules {
277 args.push("--icu-rules".into());
278 args.push(icu_rules.into());
279 }
280
281 if let Some(locale_provider) = &self.locale_provider {
282 args.push("--locale-provider".into());
283 args.push(locale_provider.into());
284 }
285
286 if let Some(owner) = &self.owner {
287 args.push("--owner".into());
288 args.push(owner.into());
289 }
290
291 if let Some(strategy) = &self.strategy {
292 args.push("--strategy".into());
293 args.push(strategy.into());
294 }
295
296 if let Some(template) = &self.template {
297 args.push("--template".into());
298 args.push(template.into());
299 }
300
301 if self.version {
302 args.push("--version".into());
303 }
304
305 if self.help {
306 args.push("--help".into());
307 }
308
309 if let Some(host) = &self.host {
310 args.push("--host".into());
311 args.push(host.into());
312 }
313
314 if let Some(port) = &self.port {
315 args.push("--port".into());
316 args.push(port.to_string().into());
317 }
318
319 if let Some(username) = &self.username {
320 args.push("--username".into());
321 args.push(username.into());
322 }
323
324 if self.no_password {
325 args.push("--no-password".into());
326 }
327
328 if self.password {
329 args.push("--password".into());
330 }
331
332 if let Some(maintenance_db) = &self.maintenance_db {
333 args.push("--maintenance-db".into());
334 args.push(maintenance_db.into());
335 }
336
337 if let Some(dbname) = &self.dbname {
338 args.push(dbname.into());
339 }
340
341 if let Some(description) = &self.description {
342 args.push(description.into());
343 }
344
345 args
346 }
347
348 fn get_envs(&self) -> Vec<(OsString, OsString)> {
350 let mut envs: Vec<(OsString, OsString)> = self.envs.clone();
351
352 if let Some(password) = &self.pg_password {
353 envs.push(("PGPASSWORD".into(), password.into()));
354 }
355
356 envs
357 }
358
359 fn env<S: AsRef<OsStr>>(mut self, key: S, value: S) -> Self {
361 self.envs
362 .push((key.as_ref().to_os_string(), value.as_ref().to_os_string()));
363 self
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370 use crate::TestSettings;
371 use crate::TestSocketSettings;
372 use crate::traits::CommandToString;
373 use test_log::test;
374
375 #[test]
376 fn test_builder_new() {
377 let command = CreateDbBuilder::new().program_dir(".").build();
378 assert_eq!(
379 PathBuf::from(".").join("createdb"),
380 PathBuf::from(command.to_command_string().replace('"', ""))
381 );
382 }
383
384 #[test]
385 fn test_builder_from() {
386 let command = CreateDbBuilder::from(&TestSettings).build();
387 #[cfg(not(target_os = "windows"))]
388 let command_prefix = r#"PGPASSWORD="password" "./createdb" "#;
389 #[cfg(target_os = "windows")]
390 let command_prefix = r#"".\\createdb" "#;
391
392 assert_eq!(
393 format!(
394 r#"{command_prefix}"--host" "localhost" "--port" "5432" "--username" "postgres""#
395 ),
396 command.to_command_string()
397 );
398 }
399
400 #[test]
401 fn test_builder() {
402 let command = CreateDbBuilder::new()
403 .env("PGDATABASE", "database")
404 .tablespace("pg_default")
405 .echo()
406 .encoding("UTF8")
407 .locale("en_US.UTF-8")
408 .lc_collate("en_US.UTF-8")
409 .lc_ctype("en_US.UTF-8")
410 .icu_locale("en_US")
411 .icu_rules("standard")
412 .locale_provider("icu")
413 .owner("postgres")
414 .strategy("wal_log")
415 .template("template0")
416 .version()
417 .help()
418 .host("localhost")
419 .port(5432)
420 .username("postgres")
421 .no_password()
422 .password()
423 .pg_password("password")
424 .maintenance_db("postgres")
425 .dbname("testdb")
426 .description("Test Database")
427 .build();
428 #[cfg(not(target_os = "windows"))]
429 let command_prefix = r#"PGDATABASE="database" PGPASSWORD="password" "#;
430 #[cfg(target_os = "windows")]
431 let command_prefix = String::new();
432
433 assert_eq!(
434 format!(
435 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""#
436 ),
437 command.to_command_string()
438 );
439 }
440
441 #[test]
442 fn test_builder_from_socket() {
443 let command = CreateDbBuilder::from(&TestSocketSettings).build();
444 #[cfg(not(target_os = "windows"))]
445 let command_prefix = r#"PGPASSWORD="password" "./createdb" "#;
446 #[cfg(target_os = "windows")]
447 let command_prefix = r#"".\\createdb" "#;
448
449 assert_eq!(
450 format!(
451 r#"{command_prefix}"--host" "/tmp/pg_socket" "--port" "5432" "--username" "postgres""#
452 ),
453 command.to_command_string()
454 );
455 }
456}