use core::fmt::Display;
use std::env;
use compact_str::ToCompactString;
use getset::{Getters, WithSetters};
use tap::{Pipe, Tap};
use crate::os_cmd::{CommandRepr, MiniStr, RunnableCommand, fmt_compact};
mod sub_cmd;
pub use sub_cmd::SubCmd;
mod cargo_profile;
pub use cargo_profile::CargoProfile;
pub mod flags;
mod target_list;
pub use target_list::{CargoTarget, RustcTarget};
mod build_std;
pub use build_std::BuildStd;
mod build_std_features;
pub use build_std_features::BuildStdFeatures;
impl RunnableCommand<'_> for CargoCmd {}
#[derive(Debug, Clone, WithSetters, Getters)]
#[getset(set_with = "pub", get = "pub with_prefix")]
pub struct CargoCmd {
rust_flags: flags::RustFlags,
nightly: bool,
cargo: MiniStr,
sub_command: SubCmd,
profile: CargoProfile,
pkg: MiniStr,
target: CargoTarget,
all_packages: bool,
all_features: bool,
no_default_features: bool,
features: Box<[MiniStr]>,
build_std: BuildStd,
build_std_features: BuildStdFeatures,
extra_args: Box<[MiniStr]>,
}
impl Default for CargoCmd {
fn default() -> Self {
Self {
rust_flags: Default::default(),
nightly: false,
cargo: "cargo".into(),
sub_command: Default::default(),
profile: Default::default(),
pkg: "".into(),
target: Default::default(),
all_packages: false,
all_features: false,
no_default_features: false,
features: Default::default(),
build_std: Default::default(),
build_std_features: Default::default(),
extra_args: Default::default(),
}
}
}
pub fn try_into_long_arg<D, S>(flag: D, value: S) -> Option<MiniStr>
where
D: Display,
S: AsRef<str>,
{
match value.as_ref() {
"" => None,
v => fmt_compact!("--{flag}={v}").into(),
}
}
pub trait ArgConverter {
type ArgsIter: Iterator<Item = MiniStr>;
fn to_args(&self) -> Self::ArgsIter;
}
impl CargoCmd {
#[allow(clippy::unnecessary_lazy_evaluations)]
pub fn into_vec(self) -> Vec<MiniStr> {
let CargoCmd {
rust_flags,
cargo,
sub_command,
nightly,
profile,
pkg,
target,
all_packages,
all_features,
no_default_features,
features,
build_std,
build_std_features,
extra_args,
} = self;
let rust_flags_value = rust_flags
.into_vec()
.join(" ")
.tap(|x| log::debug!("setenv: RUSTFLAGS={x}"));
unsafe { env::set_var("RUSTFLAGS", rust_flags_value) }
match cargo {
c if c.is_empty() => "cargo".into(),
c => c,
}
.pipe(core::iter::once)
.chain(nightly.then(|| "+nightly".into()))
.chain(
sub_command
.as_str()
.to_compact_string()
.pipe(Some),
)
.chain(try_into_long_arg("profile", profile))
.chain(try_into_long_arg("package", pkg))
.chain(all_packages.then(|| "--workspace".into()))
.chain(try_into_long_arg("target", target))
.chain(all_features.then(|| "--all-features".into()))
.chain(no_default_features.then(|| "--no-default-features".into()))
.chain(match features {
x if x.is_empty() => None,
feats => Some(fmt_compact!("--features={}", feats.join(","))),
})
.chain(build_std.to_args())
.chain(build_std_features.to_args())
.chain(extra_args)
.collect()
}
}
impl From<CargoCmd> for CommandRepr<'_> {
fn from(value: CargoCmd) -> Self {
value
.into_vec()
.into_boxed_slice()
.pipe(CommandRepr::OwnedSlice)
}
}
#[cfg(test)]
mod tests {
use tap::Pipe;
#[test]
#[ignore]
fn test_cargo_build_command() {
use crate::{
get_pkg_name,
os_cmd::{
Runner,
presets::{
CargoCmd,
cargo_build::{BuildStd, BuildStdFeatures, RustcTarget},
},
},
};
let vec = CargoCmd::default()
.with_nightly(true)
.with_pkg(get_pkg_name!().into())
.with_target(RustcTarget::aarch64_linux_android.into())
.with_build_std(
BuildStd::default()
.with_alloc(true)
.with_core(true),
)
.with_build_std_features(
BuildStdFeatures::default().with_panic_immediate_abort(true),
)
.into_vec();
assert_eq!(
vec,
[
"cargo",
"+nightly",
"build",
"--profile=release",
"--package=testutils",
"--target=aarch64-linux-android",
"-Z",
"build-std=core,alloc",
"-Z",
"build-std-features=panic_immediate_abort"
]
);
println!("{vec:?}");
let _runner: Runner = vec.into();
}
#[ignore]
#[test]
fn show_default_cargo_build() {
use crate::os_cmd::presets::CargoCmd;
CargoCmd::default().pipe(|x| dbg!(x));
}
}