temp_postgres/
error.rs

1use std::path::PathBuf;
2
3/// An error that can occur when creating or cleaning a postgres instance.
4pub struct Error {
5	/// The actual error.
6	inner: ErrorInner,
7}
8
9#[derive(Debug)]
10pub enum ErrorInner {
11	/// Failed to create the temporary directory.
12	MakeTempDir(std::io::Error),
13
14	/// Failed to run a command.
15	RunCommand(String, std::io::Error),
16
17	/// Command was run but reported failure.
18	CommandFailed(String, std::process::Output),
19
20	/// Failed to spawn the server.
21	SpawnServer(String, std::io::Error),
22
23	/// Failed to create a file.
24	Create(PathBuf, std::io::Error),
25
26	/// Failed to duplicate a file descriptor.
27	Duplicate(PathBuf, std::io::Error),
28
29	/// Failed to open a file for reading.
30	Open(PathBuf, std::io::Error),
31
32	/// Failed to read from a file.
33	Read(PathBuf, std::io::Error),
34
35	/// The server did not become ready on time.
36	ServerReadyTimeout,
37
38	/// Failed to kill the server.
39	KillServer(std::io::Error),
40
41	/// Failed to clean up the temporary directory.
42	CleanDir(PathBuf, std::io::Error),
43
44	/// Failed to connect to the server.
45	Connect(String, tokio_postgres::Error),
46}
47
48impl std::error::Error for Error {}
49
50impl std::fmt::Debug for Error {
51	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52		std::fmt::Debug::fmt(&self.inner, f)
53	}
54}
55
56impl std::fmt::Display for Error {
57	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58		std::fmt::Display::fmt(&self.inner, f)
59	}
60}
61
62impl std::fmt::Display for ErrorInner {
63	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64		match self {
65			Self::MakeTempDir(e) => write!(f, "Failed to create temporary directory: {e}"),
66			Self::RunCommand(name, e) => write!(f, "Failed run command `{name}`: {e}"),
67			Self::CommandFailed(name, output) => report_exit_reason(f, name, output),
68			Self::SpawnServer(name, e) => write!(f, "Failed to run server command: {name}: {e}"),
69			Self::Create(path, e) => write!(f, "Failed to create {}: {e}", path.display()),
70			Self::Duplicate(path, e) => write!(f, "Failed to duplicate file descriptor of {}: {e}", path.display()),
71			Self::Open(path, e) => write!(f, "Failed to open {} for reading: {e}", path.display()),
72			Self::Read(path, e) => write!(f, "Failed to read from {}: {e}", path.display()),
73			Self::ServerReadyTimeout => write!(f, "Server did not become ready within the timeout"),
74			Self::KillServer(e) => write!(f, "Failed to terminate spanwed server: {e}"),
75			Self::CleanDir(path, e) => write!(f, "Failed to clean up temporary state directory {}: {e}", path.display()),
76			Self::Connect(address, e) => write!(f, "Failed to connect to server at {address}: {e}"),
77		}
78	}
79}
80
81fn report_exit_reason(f: &mut std::fmt::Formatter, name: &str, output: &std::process::Output) -> std::fmt::Result {
82	#[cfg(unix)]
83	{
84		use std::os::unix::process::ExitStatusExt;
85		if let Some(signal) = output.status.signal() {
86			return write!(f, "Command `{name}` was killed by signal {signal}");
87		}
88	}
89	if let Some(code) = output.status.code() {
90		write!(f, "Command `{name}` exitted with status {code}")?;
91		if let Ok(error) = std::str::from_utf8(&output.stderr) {
92			let error = error.trim();
93			if !error.is_empty() {
94				write!(f, "Error: \n{error}")?;
95			}
96		}
97		Ok(())
98	} else {
99		write!(f, "Command `{name}` failed with unknown termination reason")
100	}
101}
102
103impl From<ErrorInner> for Error {
104	fn from(inner: ErrorInner) -> Self {
105		Self { inner }
106	}
107}