Skip to content

Commit d007426

Browse files
committed
Add --feature-requires flag for passing feature dependencies
1 parent f5ff22c commit d007426

File tree

8 files changed

+463
-12
lines changed

8 files changed

+463
-12
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ same-file = "1.0.1"
3434
serde_json = "1"
3535
termcolor = "1"
3636
toml_edit = "0.25"
37+
winnow = "0.7"
3738

3839
[dev-dependencies]
3940
build-context = "0.1"

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ OPTIONS:
155155

156156
This flag can only be used together with --feature-powerset flag.
157157

158+
--feature-requires <CONSTRAINTS>...
159+
Comma separated list of feature dependency constraints.
160+
161+
Each constraint has the form `FEATURE:EXPR` where EXPR is a boolean expression using
162+
AND, OR, and parentheses. AND binds tighter than OR.
163+
164+
Example: `--feature-requires "c:a OR b"` means feature c requires at least one of a or
165+
b.
166+
167+
To specify multiple constraints, separate with commas or use this option multiple
168+
times: `--feature-requires "c:a OR b,d:e AND f"`
169+
170+
This flag can only be used together with --feature-powerset flag.
171+
158172
--include-features <FEATURES>...
159173
Include only the specified features in the feature combinations instead of package
160174
features.

src/cli.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ use lexopt::{
1313
ValueExt as _,
1414
};
1515

16-
use crate::{Feature, LogGroup, Partition, Rustup, term, version::VersionRange};
16+
use crate::{
17+
Feature, LogGroup, Partition, Rustup,
18+
features::{FeatureRequirement, parse_feature_requires},
19+
term,
20+
version::VersionRange,
21+
};
1722

1823
pub(crate) struct Args {
1924
pub(crate) leading_args: Vec<String>,
@@ -91,6 +96,8 @@ pub(crate) struct Args {
9196
/// --at-least-one-of <FEATURES>...
9297
/// Implies --exclude-no-default-features. Can be specified multiple times.
9398
pub(crate) at_least_one_of: Vec<Feature>,
99+
/// --feature-requires <CONSTRAINTS>...
100+
pub(crate) feature_requires: Vec<FeatureRequirement>,
94101

95102
// options that will be propagated to cargo
96103
/// --features <FEATURES>...
@@ -179,6 +186,7 @@ impl Args {
179186

180187
let mut group_features: Vec<String> = vec![];
181188
let mut mutually_exclusive_features: Vec<String> = vec![];
189+
let mut feature_requires: Vec<String> = vec![];
182190
let mut depth = None;
183191

184192
let mut verbose = 0;
@@ -304,6 +312,7 @@ impl Args {
304312
Long("each-feature") => parse_flag!(each_feature),
305313
Long("feature-powerset") => parse_flag!(feature_powerset),
306314
Long("at-least-one-of") => at_least_one_of.push(parser.value()?.parse()?),
315+
Long("feature-requires") => feature_requires.push(parser.value()?.parse()?),
307316
Long("no-private") => parse_flag!(no_private),
308317
Long("ignore-private") => parse_flag!(ignore_private),
309318
Long("exclude-no-default-features") => parse_flag!(exclude_no_default_features),
@@ -429,6 +438,8 @@ impl Args {
429438
requires("--mutually-exclusive-features", &["--feature-powerset"])?;
430439
} else if !at_least_one_of.is_empty() {
431440
requires("--at-least-one-of", &["--feature-powerset"])?;
441+
} else if !feature_requires.is_empty() {
442+
requires("--feature-requires", &["--feature-powerset"])?;
432443
}
433444
}
434445

@@ -437,6 +448,14 @@ impl Args {
437448
let mutually_exclusive_features =
438449
parse_grouped_features(&mutually_exclusive_features, "mutually-exclusive-features")?;
439450
let at_least_one_of = parse_grouped_features(&at_least_one_of, "at-least-one-of")?;
451+
let feature_requires: Vec<FeatureRequirement> = feature_requires
452+
.iter()
453+
.map(|s| parse_feature_requires(s))
454+
.collect::<Result<Vec<_>, _>>()
455+
.map_err(|e| format_err!("{e}"))?
456+
.into_iter()
457+
.flatten()
458+
.collect();
440459

441460
if let Some(subcommand) = subcommand.as_deref() {
442461
match subcommand {
@@ -637,6 +656,7 @@ impl Args {
637656
depth,
638657
group_features,
639658
mutually_exclusive_features,
659+
feature_requires,
640660

641661
exclude_features,
642662
exclude_no_default_features,
@@ -779,6 +799,21 @@ const HELP: &[HelpText<'_>] = &[
779799
"This flag can only be used together with --feature-powerset flag.",
780800
],
781801
),
802+
(
803+
"",
804+
"--feature-requires",
805+
"<CONSTRAINTS>...",
806+
"Comma separated list of feature dependency constraints",
807+
&[
808+
"Each constraint has the form `FEATURE:EXPR` where EXPR is a boolean expression \
809+
using AND, OR, and parentheses. AND binds tighter than OR.",
810+
"Example: `--feature-requires \"c:a OR b\"` means feature c requires at least \
811+
one of a or b.",
812+
"To specify multiple constraints, separate with commas or use this option multiple times: \
813+
`--feature-requires \"c:a OR b,d:e AND f\"`",
814+
"This flag can only be used together with --feature-powerset flag.",
815+
],
816+
),
782817
(
783818
"",
784819
"--include-features",
@@ -998,6 +1033,7 @@ fn similar_flags(flag: &str, subcommand: Option<&str>) -> Result<()> {
9981033
"--include-deps-features"
9991034
}
10001035
"ignore-unknown-feature" => "--ignore-unknown-features",
1036+
"feature-require" | "features-requires" | "features-require" => "--feature-requires",
10011037
_ => return Ok(()),
10021038
};
10031039
similar_arg(&Long(flag), subcommand, expected)

0 commit comments

Comments
 (0)