Skip to content

Commit f58165f

Browse files
committed
Return State from various Cluster methods
Previously these returned `bool`, but it wasn't obvious what this meant. In addition, `createdb` and `dropdb` always returned `true`; these now return unit. Some improvements to docs too.
1 parent 89fc31a commit f58165f

File tree

3 files changed

+55
-35
lines changed

3 files changed

+55
-35
lines changed

src/cluster.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ impl Cluster {
6060
}
6161
}
6262

63+
/// Return a [`Command`] that will invoke `pg_ctl` with the environment
64+
/// referring to this cluster.
6365
fn ctl(&self) -> Result<Command, ClusterError> {
6466
let mut command = self.runtime()?.execute("pg_ctl");
6567
command.env("PGDATA", &self.datadir);
@@ -180,18 +182,18 @@ impl Cluster {
180182
}
181183

182184
/// Create the cluster if it does not already exist.
183-
pub fn create(&self) -> Result<bool, ClusterError> {
185+
pub fn create(&self) -> Result<State, ClusterError> {
184186
match self._create() {
185-
Err(ClusterError::UnixError(Errno::EAGAIN)) if exists(self) => Ok(false),
187+
Err(ClusterError::UnixError(Errno::EAGAIN)) if exists(self) => Ok(Unmodified),
186188
Err(ClusterError::UnixError(Errno::EAGAIN)) => Err(ClusterError::InUse),
187189
other => other,
188190
}
189191
}
190192

191-
fn _create(&self) -> Result<bool, ClusterError> {
193+
fn _create(&self) -> Result<State, ClusterError> {
192194
if exists(self) {
193195
// Nothing more to do; the cluster is already in place.
194-
Ok(false)
196+
Ok(Unmodified)
195197
} else {
196198
// Create the cluster and report back that we did so.
197199
fs::create_dir_all(&self.datadir)?;
@@ -206,26 +208,26 @@ impl Cluster {
206208
.arg("-E utf8 --locale C -A trust")
207209
.env("TZ", "UTC")
208210
.output()?;
209-
Ok(true)
211+
Ok(Modified)
210212
}
211213
}
212214

213-
// Start the cluster if it's not already running.
214-
pub fn start(&self) -> Result<bool, ClusterError> {
215+
/// Start the cluster if it's not already running.
216+
pub fn start(&self) -> Result<State, ClusterError> {
215217
match self._start() {
216-
Err(ClusterError::UnixError(Errno::EAGAIN)) if self.running()? => Ok(false),
218+
Err(ClusterError::UnixError(Errno::EAGAIN)) if self.running()? => Ok(Unmodified),
217219
Err(ClusterError::UnixError(Errno::EAGAIN)) => Err(ClusterError::InUse),
218220
other => other,
219221
}
220222
}
221223

222-
fn _start(&self) -> Result<bool, ClusterError> {
224+
fn _start(&self) -> Result<State, ClusterError> {
223225
// Ensure that the cluster has been created.
224226
self._create()?;
225227
// Check if we're running already.
226228
if self.running()? {
227229
// We didn't start this cluster; say so.
228-
return Ok(false);
230+
return Ok(Unmodified);
229231
}
230232
// Next, invoke `pg_ctl` to start the cluster.
231233
// pg_ctl options:
@@ -249,10 +251,10 @@ impl Cluster {
249251
})
250252
.output()?;
251253
// We did actually start the cluster; say so.
252-
Ok(true)
254+
Ok(Modified)
253255
}
254256

255-
// Connect to this cluster.
257+
/// Connect to this cluster.
256258
pub fn connect(&self, database: &str) -> Result<postgres::Client, ClusterError> {
257259
let user = &env::var("USER").unwrap_or_else(|_| "USER-not-set".to_string());
258260
let host = self.datadir.to_string_lossy(); // postgres crate API limitation.
@@ -264,6 +266,7 @@ impl Cluster {
264266
Ok(client)
265267
}
266268

269+
/// Run `psql` against this cluster, in the given database.
267270
pub fn shell(&self, database: &str) -> Result<ExitStatus, ClusterError> {
268271
let mut command = self.runtime()?.execute("psql");
269272
command.arg("--quiet");
@@ -273,6 +276,10 @@ impl Cluster {
273276
Ok(command.spawn()?.wait()?)
274277
}
275278

279+
/// Run the given command against this cluster.
280+
///
281+
/// The command is run with the `PGDATA`, `PGHOST`, and `PGDATABASE`
282+
/// environment variables set appropriately.
276283
pub fn exec<T: AsRef<OsStr>>(
277284
&self,
278285
database: &str,
@@ -287,7 +294,7 @@ impl Cluster {
287294
Ok(command.spawn()?.wait()?)
288295
}
289296

290-
// The names of databases in this cluster.
297+
/// The names of databases in this cluster.
291298
pub fn databases(&self) -> Result<Vec<String>, ClusterError> {
292299
let mut conn = self.connect("template1")?;
293300
let rows = conn.query(
@@ -299,40 +306,40 @@ impl Cluster {
299306
}
300307

301308
/// Create the named database.
302-
pub fn createdb(&self, database: &str) -> Result<bool, ClusterError> {
309+
pub fn createdb(&self, database: &str) -> Result<(), ClusterError> {
303310
let statement = format!(
304311
"CREATE DATABASE {}",
305312
postgres_protocol::escape::escape_identifier(database)
306313
);
307314
self.connect("template1")?
308315
.execute(statement.as_str(), &[])?;
309-
Ok(true)
316+
Ok(())
310317
}
311318

312319
/// Drop the named database.
313-
pub fn dropdb(&self, database: &str) -> Result<bool, ClusterError> {
320+
pub fn dropdb(&self, database: &str) -> Result<(), ClusterError> {
314321
let statement = format!(
315322
"DROP DATABASE {}",
316323
postgres_protocol::escape::escape_identifier(database)
317324
);
318325
self.connect("template1")?
319326
.execute(statement.as_str(), &[])?;
320-
Ok(true)
327+
Ok(())
321328
}
322329

323-
// Stop the cluster if it's running.
324-
pub fn stop(&self) -> Result<bool, ClusterError> {
330+
/// Stop the cluster if it's running.
331+
pub fn stop(&self) -> Result<State, ClusterError> {
325332
match self._stop() {
326-
Err(ClusterError::UnixError(Errno::EAGAIN)) if !self.running()? => Ok(false),
333+
Err(ClusterError::UnixError(Errno::EAGAIN)) if !self.running()? => Ok(Unmodified),
327334
Err(ClusterError::UnixError(Errno::EAGAIN)) => Err(ClusterError::InUse),
328335
other => other,
329336
}
330337
}
331338

332-
fn _stop(&self) -> Result<bool, ClusterError> {
339+
fn _stop(&self) -> Result<State, ClusterError> {
333340
// If the cluster's not already running, don't do anything.
334341
if !self.running()? {
335-
return Ok(false);
342+
return Ok(Unmodified);
336343
}
337344
// pg_ctl options:
338345
// -w -- wait for shutdown to complete.
@@ -344,23 +351,23 @@ impl Cluster {
344351
.arg("-m")
345352
.arg("fast")
346353
.output()?;
347-
Ok(true)
354+
Ok(Modified)
348355
}
349356

350-
// Destroy the cluster if it exists, after stopping it.
351-
pub fn destroy(&self) -> Result<bool, ClusterError> {
357+
/// Destroy the cluster if it exists, after stopping it.
358+
pub fn destroy(&self) -> Result<State, ClusterError> {
352359
match self._destroy() {
353360
Err(ClusterError::UnixError(Errno::EAGAIN)) => Err(ClusterError::InUse),
354361
other => other,
355362
}
356363
}
357364

358-
fn _destroy(&self) -> Result<bool, ClusterError> {
359-
if self._stop()? || self.datadir.is_dir() {
365+
fn _destroy(&self) -> Result<State, ClusterError> {
366+
if self._stop()? == Modified || self.datadir.is_dir() {
360367
fs::remove_dir_all(&self.datadir)?;
361-
Ok(true)
368+
Ok(Modified)
362369
} else {
363-
Ok(false)
370+
Ok(Unmodified)
364371
}
365372
}
366373
}
@@ -371,6 +378,19 @@ impl AsRef<Path> for Cluster {
371378
}
372379
}
373380

381+
#[derive(Debug, PartialEq, Eq)]
382+
pub enum State {
383+
/// The action we requested was performed from this process, e.g. we tried
384+
/// to create the cluster, and we did indeed create the cluster.
385+
Modified,
386+
/// The action we requested was performed by another process, or was not
387+
/// necessary, e.g. we tried to stop the cluster but it was already stopped.
388+
Unmodified,
389+
}
390+
391+
// For convenience.
392+
use State::{Modified, Unmodified};
393+
374394
/// A fairly simplistic but quick check: does the directory exist and does it
375395
/// look like a PostgreSQL cluster data directory, i.e. does it contain a file
376396
/// named `PG_VERSION`?

src/cluster/tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{exists, version, Cluster, ClusterError};
1+
use super::{exists, version, Cluster, ClusterError, State::*};
22
use crate::runtime::{self, strategy::Strategy, Runtime};
33
use crate::version::{PartialVersion, Version};
44

@@ -110,7 +110,7 @@ fn cluster_create_creates_cluster() -> TestResult {
110110
let data_dir = tempdir::TempDir::new("data")?;
111111
let cluster = Cluster::new(&data_dir, runtime)?;
112112
assert!(!exists(&cluster));
113-
assert!(cluster.create()?);
113+
assert!(cluster.create()? == Modified);
114114
assert!(exists(&cluster));
115115
}
116116
Ok(())
@@ -192,9 +192,9 @@ fn cluster_create_does_nothing_when_it_already_exists() -> TestResult {
192192
let data_dir = tempdir::TempDir::new("data")?;
193193
let cluster = Cluster::new(&data_dir, runtime)?;
194194
assert!(!exists(&cluster));
195-
assert!(cluster.create()?);
195+
assert!(cluster.create()? == Modified);
196196
assert!(exists(&cluster));
197-
assert!(!cluster.create()?);
197+
assert!(cluster.create()? == Unmodified);
198198
}
199199
Ok(())
200200
}

src/coordinate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::time::Duration;
2121
use either::Either::{Left, Right};
2222
use rand::RngCore;
2323

24-
use crate::cluster::{Cluster, ClusterError};
24+
use crate::cluster::{Cluster, ClusterError, State};
2525
use crate::lock;
2626

2727
/// Perform `action` in `cluster`.
@@ -41,7 +41,7 @@ where
4141
{
4242
let lock = startup(cluster, lock)?;
4343
let action_res = std::panic::catch_unwind(|| action(cluster));
44-
let _: Option<bool> = shutdown(cluster, lock, Cluster::stop)?;
44+
let _: Option<State> = shutdown(cluster, lock, Cluster::stop)?;
4545
match action_res {
4646
Ok(result) => Ok(result),
4747
Err(err) => std::panic::resume_unwind(err),

0 commit comments

Comments
 (0)