use std::collections::BTreeMap;
use std::path::PathBuf;
use std::{env, process};
#[derive(Default, Clone, Debug)]
pub struct Cargo {
subcmd: String,
features: clap_cargo::Features,
stdio: [Stdio; 3],
manifest: Option<PathBuf>,
package: String,
log_level: Option<String>,
target: Option<String>,
profile: CargoProfile,
more_args: BTreeMap<String, Vec<String>>,
}
impl Cargo {
pub fn subcommand(mut self, s: &str) -> Self {
self.subcmd.clear();
self.subcmd.push_str(s);
self
}
pub fn std_streams(mut self, streams: [Stdio; 3]) -> Self {
self.stdio = streams;
self
}
pub fn manifest_path(mut self, path: Option<PathBuf>) -> Self {
self.manifest = path;
self
}
pub fn package(mut self, package: String) -> Self {
self.package = package;
self
}
pub fn target(mut self, target: Option<String>) -> Self {
self.target = target;
self
}
pub fn profile(mut self, profile: CargoProfile) -> Self {
self.profile = profile;
self
}
pub fn log_level(mut self, level: Option<String>) -> Self {
self.log_level = level;
self
}
pub fn flag(mut self, flag: impl Into<String>) -> Self {
self.more_args.insert(flag.into(), Vec::new());
self
}
pub fn features(mut self, features: clap_cargo::Features) -> Self {
self.features = features;
self
}
#[track_caller]
pub fn into_command(self) -> process::Command {
let mut cmd = cargo();
if self.subcmd != "" {
cmd.arg(&self.subcmd);
} else {
panic!("`Cargo::into_command` requires a subcommand to be set, was: {self:?}")
}
let Cargo {
features,
stdio,
manifest,
log_level,
target,
profile,
package,
subcmd: _,
more_args,
} = self;
cmd.args(profile.cargo_args());
if let Some(target) = target {
cmd.arg("--target").arg(target);
}
if let Some(manifest) = manifest {
cmd.arg("--manifest-path").arg(manifest);
}
if !package.is_empty() {
cmd.arg("--package").arg(package);
}
let [stdin, stdout, stderr] = stdio;
if let Some(stdio) = stdin.into_stdio() {
cmd.stdin(stdio);
}
if let Some(stdio) = stdout.into_stdio() {
cmd.stdout(stdio);
}
if let Some(stdio) = stderr.into_stdio() {
cmd.stderr(stdio);
}
if features.no_default_features {
cmd.arg("--no-default-features");
}
if features.all_features {
cmd.arg("--all-features");
}
if !features.features.is_empty() {
cmd.arg("--features");
cmd.arg(features.features.join(" "));
}
let flags = env::var("PGRX_BUILD_FLAGS").unwrap_or_default();
for arg in flags.split_ascii_whitespace() {
cmd.arg(arg);
}
if let Some(log_level) = log_level {
cmd.env("RUST_LOG", log_level);
}
for (flag, args) in more_args {
cmd.arg(flag).args(args);
}
cmd
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum Stdio {
Inherit,
Null,
#[default]
Default,
}
impl Stdio {
fn into_stdio(self) -> Option<process::Stdio> {
match self {
Stdio::Inherit => Some(process::Stdio::inherit()),
Stdio::Null => Some(process::Stdio::null()),
Stdio::Default => None,
}
}
}
pub(crate) fn cargo() -> std::process::Command {
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
std::process::Command::new(cargo)
}
pub(crate) fn initialize() {
match (std::env::var_os("CARGO_PGRX"), std::env::current_exe()) {
(None, Ok(path)) => {
unsafe {
std::env::set_var("CARGO_PGRX", path);
}
}
(Some(_), Ok(_)) => {
}
(_, Err(_)) => {}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum CargoProfile {
#[default]
Dev,
Release,
Profile(String),
}
impl CargoProfile {
pub fn from_flags(profile: Option<&str>, default: CargoProfile) -> eyre::Result<Self> {
match profile {
Some("release") => Ok(Self::Release),
Some("debug") | Some("dev") => Ok(Self::Dev),
Some(profile) => Ok(Self::Profile(profile.into())),
None => Ok(default),
}
}
pub fn cargo_args(&self) -> Vec<String> {
match self {
Self::Dev => vec![],
Self::Release => vec!["--release".into()],
Self::Profile(p) => vec!["--profile".into(), p.into()],
}
}
pub fn name(&self) -> &str {
match self {
Self::Dev => "dev",
Self::Release => "release",
Self::Profile(p) => p,
}
}
pub fn target_subdir(&self) -> &str {
match self {
Self::Dev => "debug",
Self::Release => "release",
Self::Profile(p) => p,
}
}
}