blob: 21a7513d2816289cf85ddbbf0c490bdd54ee2bf0 [file] [log] [blame]
//! Cargo Feature Flags.
#[derive(Default, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[non_exhaustive]
pub struct Features {
#[cfg_attr(feature = "clap", arg(long))]
/// Activate all available features
pub all_features: bool,
#[cfg_attr(feature = "clap", arg(long))]
/// Do not activate the `default` feature
pub no_default_features: bool,
#[cfg_attr(feature = "clap", arg(short = 'F', long, value_delimiter = ' '))]
/// Space-separated list of features to activate
pub features: Vec<String>,
}
#[cfg(feature = "cargo_metadata")]
impl Features {
/// Forward these flags to the `cargo_metadata` crate.
///
/// Note: Requires the features `cargo_metadata`.
pub fn forward_metadata<'m>(
&self,
meta: &'m mut cargo_metadata::MetadataCommand,
) -> &'m mut cargo_metadata::MetadataCommand {
if self.all_features {
meta.features(cargo_metadata::CargoOpt::AllFeatures);
}
if self.no_default_features {
meta.features(cargo_metadata::CargoOpt::NoDefaultFeatures);
}
if !self.features.is_empty() {
meta.features(cargo_metadata::CargoOpt::SomeFeatures(
self.features.clone(),
));
}
meta
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(feature = "clap")]
fn verify_app() {
#[derive(Debug, clap::Parser)]
struct Cli {
#[command(flatten)]
features: Features,
}
use clap::CommandFactory;
Cli::command().debug_assert()
}
#[test]
#[cfg(feature = "clap")]
fn parse_multiple_occurrences() {
use clap::Parser;
#[derive(PartialEq, Eq, Debug, Parser)]
struct Args {
positional: Option<String>,
#[command(flatten)]
features: Features,
}
assert_eq!(
Args {
positional: None,
features: Features {
all_features: false,
no_default_features: false,
features: vec![]
}
},
Args::parse_from(["test"])
);
assert_eq!(
Args {
positional: Some("foo".to_owned()),
features: Features {
all_features: false,
no_default_features: false,
features: vec![]
}
},
Args::parse_from(["test", "foo"])
);
assert_eq!(
Args {
positional: None,
features: Features {
all_features: false,
no_default_features: false,
features: vec!["foo".to_owned()]
}
},
Args::parse_from(["test", "--features", "foo"])
);
assert_eq!(
Args {
positional: None,
features: Features {
all_features: false,
no_default_features: false,
features: vec!["foo".to_owned(), "bar".to_owned()]
}
},
Args::parse_from(["test", "--features", "foo bar"])
);
assert_eq!(
Args {
positional: Some("baz".to_owned()),
features: Features {
all_features: false,
no_default_features: false,
features: vec!["foo".to_owned(), "bar".to_owned()]
}
},
Args::parse_from(["test", "--features", "foo bar", "baz"])
);
assert_eq!(
Args {
positional: Some("baz".to_owned()),
features: Features {
all_features: false,
no_default_features: false,
features: vec!["foo".to_owned(), "bar".to_owned()]
}
},
Args::parse_from(["test", "--features", "foo", "--features", "bar", "baz"])
);
}
#[cfg(feature = "cargo_metadata")]
#[test]
fn features_all() {
let mut metadata = cargo_metadata::MetadataCommand::new();
metadata.manifest_path("tests/fixtures/simple/Cargo.toml");
let features = Features {
all_features: true,
..Default::default()
};
features.forward_metadata(&mut metadata);
metadata.exec().unwrap();
// TODO verify we forwarded correctly.
}
#[cfg(feature = "cargo_metadata")]
#[test]
fn features_none() {
let mut metadata = cargo_metadata::MetadataCommand::new();
metadata.manifest_path("tests/fixtures/simple/Cargo.toml");
let features = Features {
no_default_features: true,
..Default::default()
};
features.forward_metadata(&mut metadata);
metadata.exec().unwrap();
// TODO verify we forwarded correctly.
}
}