use cargo_metadata::Metadata;
use cargo_toml::Manifest;
use clap_cargo::Features;
use eyre::{eyre, Context};
use pgx_pg_config::{PgConfig, Pgx};
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub(crate) enum PgVersionSource {
CliArgument(String),
FeatureFlag(String),
DefaultFeature(String),
PgConfig(String),
}
impl From<PgVersionSource> for String {
fn from(v: PgVersionSource) -> Self {
match v {
PgVersionSource::CliArgument(s) => s,
PgVersionSource::FeatureFlag(s) => s,
PgVersionSource::DefaultFeature(s) => s,
PgVersionSource::PgConfig(s) => s,
}
}
}
impl PgVersionSource {
fn label(&self) -> &String {
match self {
PgVersionSource::CliArgument(s) => s,
PgVersionSource::FeatureFlag(s) => s,
PgVersionSource::DefaultFeature(s) => s,
PgVersionSource::PgConfig(s) => s,
}
}
}
#[tracing::instrument(skip_all)]
pub(crate) fn manifest_path(
metadata: &Metadata,
package_name: Option<&String>,
) -> eyre::Result<PathBuf> {
let manifest_path = if let Some(package_name) = package_name {
let found = metadata
.packages
.iter()
.find(|v| v.name == *package_name)
.ok_or_else(|| eyre!("Could not find package `{}`", package_name))?;
tracing::debug!(manifest_path = %found.manifest_path, "Found workspace package");
found.manifest_path.clone().into_std_path_buf()
} else {
let root = metadata.root_package().ok_or(eyre!(
"`pgx` requires a root package in a workspace when `--package` is not specified."
))?;
tracing::debug!(manifest_path = %root.manifest_path, "Found root package");
root.manifest_path.clone().into_std_path_buf()
};
Ok(manifest_path)
}
pub(crate) fn modify_features_for_version(
pgx: &Pgx,
features: Option<&mut Features>,
manifest: &Manifest,
pg_version: &PgVersionSource,
test: bool,
) {
if let Some(features) = features {
if let Some(default_features) = manifest.features.get("default") {
if !features.no_default_features {
features.no_default_features = true;
features.features.extend(
default_features
.iter()
.filter(|flag| !pgx.is_feature_flag(flag))
.cloned(),
);
}
}
if test {
features.features.retain(|flag| {
if manifest.features.contains_key(flag) {
true
} else {
use owo_colors::OwoColorize;
println!(
"{} feature `{}`",
" Ignoring".bold().yellow(),
flag.bold().white()
);
false
}
});
}
if !features.features.contains(pg_version.label()) {
features.features.push(pg_version.label().clone());
}
}
}
pub(crate) fn pg_config_and_version<'a>(
pgx: &'a Pgx,
manifest: &Manifest,
specified_pg_version: Option<String>,
user_features: Option<&mut Features>,
verbose: bool,
) -> eyre::Result<(PgConfig, PgVersionSource)> {
let pg_version = {
'outer: loop {
if let Some(pg_version) = specified_pg_version {
break 'outer Some(PgVersionSource::CliArgument(pg_version));
} else if let Some(features) = user_features.as_ref() {
for flag in &features.features {
if pgx.is_feature_flag(flag) {
break 'outer Some(PgVersionSource::FeatureFlag(flag.clone()));
}
}
if !features.no_default_features {
if let Some(default_features) = manifest.features.get("default") {
for flag in default_features {
if pgx.is_feature_flag(flag) {
break 'outer Some(PgVersionSource::DefaultFeature(flag.clone()));
}
}
}
}
} else {
if let Some(default_features) = manifest.features.get("default") {
for flag in default_features {
if pgx.is_feature_flag(flag) {
break 'outer Some(PgVersionSource::DefaultFeature(flag.clone()));
}
}
}
}
break 'outer None;
}
};
match pg_version {
Some(pg_version) => {
modify_features_for_version(pgx, user_features, manifest, &pg_version, false);
let pg_config = pgx.get(&pg_version.label())?;
if verbose {
display_version_info(&pg_config, &pg_version);
}
Ok((pg_config, pg_version))
}
None => Err(eyre!("Could not determine which Postgres version feature flag to use")),
}
}
pub(crate) fn display_version_info(pg_config: &PgConfig, pg_version: &PgVersionSource) {
use owo_colors::OwoColorize;
eprintln!(
"{} {:?} and `pg_config` from {}",
" Using".bold().green(),
pg_version.bold().white(),
pg_config.path().unwrap().display().cyan()
);
}
pub(crate) fn get_package_manifest(
features: &Features,
package_nane: Option<&String>,
manifest_path: Option<impl AsRef<std::path::Path>>,
) -> eyre::Result<(Manifest, PathBuf)> {
let metadata = crate::metadata::metadata(&features, manifest_path.as_ref())
.wrap_err("couldn't get cargo metadata")?;
crate::metadata::validate(&metadata)?;
let package_manifest_path = crate::manifest::manifest_path(&metadata, package_nane)
.wrap_err("Couldn't get manifest path")?;
Ok((
Manifest::from_path(&package_manifest_path).wrap_err("Couldn't parse manifest")?,
package_manifest_path,
))
}