postgresql_commands/
clusterdb.rs

1use crate::Settings;
2use crate::traits::CommandBuilder;
3use std::convert::AsRef;
4use std::ffi::{OsStr, OsString};
5use std::path::PathBuf;
6
7/// `clusterdb` clusters all previously clustered tables in a database.
8#[derive(Clone, Debug, Default)]
9pub struct ClusterDbBuilder {
10    program_dir: Option<PathBuf>,
11    envs: Vec<(OsString, OsString)>,
12    all: bool,
13    dbname: Option<OsString>,
14    echo: bool,
15    quiet: bool,
16    table: Option<OsString>,
17    verbose: bool,
18    version: bool,
19    help: bool,
20    host: Option<OsString>,
21    port: Option<u16>,
22    username: Option<OsString>,
23    no_password: bool,
24    password: bool,
25    pg_password: Option<OsString>,
26    maintenance_db: Option<OsString>,
27}
28
29impl ClusterDbBuilder {
30    /// Create a new [`ClusterDbBuilder`]
31    #[must_use]
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Create a new [`ClusterDbBuilder`] from [Settings]
37    pub fn from(settings: &dyn Settings) -> Self {
38        Self::new()
39            .program_dir(settings.get_binary_dir())
40            .host(settings.get_host())
41            .port(settings.get_port())
42            .username(settings.get_username())
43            .pg_password(settings.get_password())
44    }
45
46    /// Location of the program binary
47    #[must_use]
48    fn program_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
49        self.program_dir = Some(path.into());
50        self
51    }
52
53    /// Cluster all databases
54    #[must_use]
55    pub fn all(mut self) -> Self {
56        self.all = true;
57        self
58    }
59
60    /// Database to cluster
61    #[must_use]
62    pub fn dbname<S: AsRef<OsStr>>(mut self, dbname: S) -> Self {
63        self.dbname = Some(dbname.as_ref().to_os_string());
64        self
65    }
66
67    /// Show the commands being sent to the server
68    #[must_use]
69    pub fn echo(mut self) -> Self {
70        self.echo = true;
71        self
72    }
73
74    /// Don't write any messages
75    #[must_use]
76    pub fn quiet(mut self) -> Self {
77        self.quiet = true;
78        self
79    }
80
81    /// Cluster specific table(s) only
82    #[must_use]
83    pub fn table<S: AsRef<OsStr>>(mut self, table: S) -> Self {
84        self.table = Some(table.as_ref().to_os_string());
85        self
86    }
87
88    /// Write a lot of output
89    #[must_use]
90    pub fn verbose(mut self) -> Self {
91        self.verbose = true;
92        self
93    }
94
95    /// Output version information, then exit
96    #[must_use]
97    pub fn version(mut self) -> Self {
98        self.version = true;
99        self
100    }
101
102    /// Show help, then exit
103    #[must_use]
104    pub fn help(mut self) -> Self {
105        self.help = true;
106        self
107    }
108
109    /// Database server host or socket directory
110    #[must_use]
111    pub fn host<S: AsRef<OsStr>>(mut self, host: S) -> Self {
112        self.host = Some(host.as_ref().to_os_string());
113        self
114    }
115
116    /// Database server port
117    #[must_use]
118    pub fn port(mut self, port: u16) -> Self {
119        self.port = Some(port);
120        self
121    }
122
123    /// User name to connect as
124    #[must_use]
125    pub fn username<S: AsRef<OsStr>>(mut self, username: S) -> Self {
126        self.username = Some(username.as_ref().to_os_string());
127        self
128    }
129
130    /// Never prompt for password
131    #[must_use]
132    pub fn no_password(mut self) -> Self {
133        self.no_password = true;
134        self
135    }
136
137    /// Force password prompt
138    #[must_use]
139    pub fn password(mut self) -> Self {
140        self.password = true;
141        self
142    }
143
144    /// user password
145    #[must_use]
146    pub fn pg_password<S: AsRef<OsStr>>(mut self, pg_password: S) -> Self {
147        self.pg_password = Some(pg_password.as_ref().to_os_string());
148        self
149    }
150
151    /// Alternate maintenance database
152    #[must_use]
153    pub fn maintenance_db<S: AsRef<OsStr>>(mut self, db: S) -> Self {
154        self.maintenance_db = Some(db.as_ref().to_os_string());
155        self
156    }
157}
158
159impl CommandBuilder for ClusterDbBuilder {
160    /// Get the program name
161    fn get_program(&self) -> &'static OsStr {
162        "clusterdb".as_ref()
163    }
164
165    /// Location of the program binary
166    fn get_program_dir(&self) -> &Option<PathBuf> {
167        &self.program_dir
168    }
169
170    /// Get the arguments for the command
171    fn get_args(&self) -> Vec<OsString> {
172        let mut args: Vec<OsString> = Vec::new();
173
174        if self.all {
175            args.push("--all".into());
176        }
177
178        if let Some(dbname) = &self.dbname {
179            args.push("--dbname".into());
180            args.push(dbname.into());
181        }
182
183        if self.echo {
184            args.push("--echo".into());
185        }
186
187        if self.quiet {
188            args.push("--quiet".into());
189        }
190
191        if let Some(table) = &self.table {
192            args.push("--table".into());
193            args.push(table.into());
194        }
195
196        if self.verbose {
197            args.push("--verbose".into());
198        }
199
200        if self.version {
201            args.push("--version".into());
202        }
203
204        if self.help {
205            args.push("--help".into());
206        }
207
208        if let Some(host) = &self.host {
209            args.push("--host".into());
210            args.push(host.into());
211        }
212
213        if let Some(port) = &self.port {
214            args.push("--port".into());
215            args.push(port.to_string().into());
216        }
217
218        if let Some(username) = &self.username {
219            args.push("--username".into());
220            args.push(username.into());
221        }
222
223        if self.no_password {
224            args.push("--no-password".into());
225        }
226
227        if self.password {
228            args.push("--password".into());
229        }
230
231        if let Some(maintenance_db) = &self.maintenance_db {
232            args.push("--maintenance-db".into());
233            args.push(maintenance_db.into());
234        }
235
236        args
237    }
238
239    /// Get the environment variables for the command
240    fn get_envs(&self) -> Vec<(OsString, OsString)> {
241        let mut envs: Vec<(OsString, OsString)> = self.envs.clone();
242
243        if let Some(password) = &self.pg_password {
244            envs.push(("PGPASSWORD".into(), password.into()));
245        }
246
247        envs
248    }
249
250    /// Set an environment variable for the command
251    fn env<S: AsRef<OsStr>>(mut self, key: S, value: S) -> Self {
252        self.envs
253            .push((key.as_ref().to_os_string(), value.as_ref().to_os_string()));
254        self
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::TestSettings;
262    use crate::traits::CommandToString;
263    use test_log::test;
264
265    #[test]
266    fn test_builder_new() {
267        let command = ClusterDbBuilder::new().program_dir(".").build();
268        assert_eq!(
269            PathBuf::from(".").join("clusterdb"),
270            PathBuf::from(command.to_command_string().replace('"', ""))
271        );
272    }
273
274    #[test]
275    fn test_builder_from() {
276        let command = ClusterDbBuilder::from(&TestSettings).build();
277        #[cfg(not(target_os = "windows"))]
278        let command_prefix = r#"PGPASSWORD="password" "./clusterdb" "#;
279        #[cfg(target_os = "windows")]
280        let command_prefix = r#"".\\clusterdb" "#;
281
282        assert_eq!(
283            format!(
284                r#"{command_prefix}"--host" "localhost" "--port" "5432" "--username" "postgres""#
285            ),
286            command.to_command_string()
287        );
288    }
289
290    #[test]
291    fn test_builder() {
292        let command = ClusterDbBuilder::new()
293            .env("PGDATABASE", "database")
294            .all()
295            .dbname("dbname")
296            .echo()
297            .quiet()
298            .table("table")
299            .verbose()
300            .version()
301            .help()
302            .host("localhost")
303            .port(5432)
304            .username("postgres")
305            .no_password()
306            .password()
307            .pg_password("password")
308            .maintenance_db("postgres")
309            .build();
310        #[cfg(not(target_os = "windows"))]
311        let command_prefix = r#"PGDATABASE="database" PGPASSWORD="password" "#;
312        #[cfg(target_os = "windows")]
313        let command_prefix = String::new();
314
315        assert_eq!(
316            format!(
317                r#"{command_prefix}"clusterdb" "--all" "--dbname" "dbname" "--echo" "--quiet" "--table" "table" "--verbose" "--version" "--help" "--host" "localhost" "--port" "5432" "--username" "postgres" "--no-password" "--password" "--maintenance-db" "postgres""#
318            ),
319            command.to_command_string()
320        );
321    }
322}