//! 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, } #[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, #[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. } }