use std::{
collections::BTreeSet,
ffi::OsString,
path::{Component, Path},
};
#[cfg(test)]
mod tests;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CargoFeatureSelection {
pub features: BTreeSet<String>,
pub all_features: bool,
pub default_features: bool,
}
impl Default for CargoFeatureSelection {
fn default() -> Self {
Self {
features: BTreeSet::new(),
default_features: true,
all_features: false,
}
}
}
impl CargoFeatureSelection {
pub(crate) fn arguments(&self) -> Vec<OsString> {
let mut arguments = Vec::new();
if self.all_features {
arguments.push("--all-features".into());
}
if !self.default_features {
arguments.push("--no-default-features".into());
}
if !self.features.is_empty() {
arguments.push(
format!(
"--features={}",
self.features
.iter()
.map(String::as_str)
.collect::<Vec<_>>()
.join(",")
)
.into(),
);
}
arguments
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CargoSelection {
pub profile: Option<String>,
pub features: CargoFeatureSelection,
pub locked: bool,
pub offline: bool,
}
impl CargoSelection {
pub(crate) fn profile_target_dir(&self) -> Result<&str, &'static str> {
let profile = self.profile.as_deref().unwrap_or("dev");
let directory = match profile {
"dev" | "test" => "debug",
"bench" => "release",
custom => custom,
};
let mut components = Path::new(directory).components();
if !matches!(
(components.next(), components.next()),
(Some(Component::Normal(_)), None)
) {
return Err("Cargo profile must select one normal output directory component");
}
Ok(directory)
}
pub(crate) fn metadata_arguments(&self) -> Vec<OsString> {
graph_arguments(&self.features, self.locked, self.offline)
}
pub(crate) fn build_arguments(&self) -> Vec<OsString> {
let mut arguments = Vec::new();
if let Some(profile) = &self.profile {
arguments.push(if profile == "release" {
"--release".into()
} else {
format!("--profile={profile}").into()
});
}
arguments.extend(self.metadata_arguments());
arguments
}
}
pub(crate) fn graph_arguments(
features: &CargoFeatureSelection,
locked: bool,
offline: bool,
) -> Vec<OsString> {
let mut arguments = Vec::new();
for (enabled, flag) in [(locked, "--locked"), (offline, "--offline")] {
if enabled {
arguments.push(flag.into());
}
}
arguments.extend(features.arguments());
arguments
}