use std::process::Command;
use camino::Utf8Path;
use color_eyre::eyre::eyre;
use postgresql_embedded::Version;
use super::extension_error;
use crate::error::{BootstrapErrorKind, BootstrapResult};
pub fn running_version(install_dir: &Utf8Path) -> BootstrapResult<Version> {
if let Some(version) = version_from_dir_name(install_dir) {
return Ok(version);
}
let output = Command::new(install_dir.join("bin").join("pg_config"))
.arg("--version")
.output()
.map_err(|err| unknown(install_dir, &format!("pg_config could not run: {err}")))?;
let text = String::from_utf8_lossy(&output.stdout);
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(unknown(
install_dir,
&format!("pg_config exited with {}: {}", output.status, stderr.trim()),
));
}
parse_pg_config_version(&text).ok_or_else(|| {
unknown(
install_dir,
&format!("pg_config reported {:?}", text.trim()),
)
})
}
fn version_from_dir_name(install_dir: &Utf8Path) -> Option<Version> {
Version::parse(install_dir.file_name()?).ok()
}
#[must_use]
pub fn parse_pg_config_version(text: &str) -> Option<Version> {
let token = text.split_whitespace().nth(1)?;
let numeric: String = token
.chars()
.take_while(|ch| ch.is_ascii_digit() || *ch == '.')
.collect();
let mut parts = numeric.split('.').filter(|part| !part.is_empty());
let major: u64 = parts.next()?.parse().ok()?;
let minor: u64 = parts.next().map_or(Some(0), |part| part.parse().ok())?;
Some(Version::new(major, minor, 0))
}
fn unknown(install_dir: &Utf8Path, detail: &str) -> crate::error::BootstrapError {
extension_error(
BootstrapErrorKind::ExtensionUnavailable,
eyre!("cannot determine the PostgreSQL version installed at {install_dir}: {detail}"),
)
}