Skip to content

Commit 0822415

Browse files
committed
Fix issue-11010
1 parent 35ea623 commit 0822415

File tree

16 files changed

+171
-0
lines changed

16 files changed

+171
-0
lines changed

src/cargo/ops/cargo_add/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
196196
print_dep_table_msg(&mut options.config.shell(), &dep)?;
197197

198198
manifest.insert_into_table(&dep_table, &dep)?;
199+
if let Some(option) = dep.optional {
200+
let rust_version_check =
201+
check_rust_version_for_optional_dependency(workspace.rust_version())?;
202+
if option && rust_version_check {
203+
let features_table: Vec<String> = vec![String::from("features")];
204+
manifest.insert_into_feature_table(&features_table, &dep)?;
205+
}
206+
}
199207
manifest.gc_dep(dep.toml_key());
200208
}
201209

@@ -469,6 +477,26 @@ fn check_invalid_ws_keys(toml_key: &str, arg: &DepOp) -> CargoResult<()> {
469477
Ok(())
470478
}
471479

480+
/// When the `--optional` option is added using `cargo add`, we need to
481+
/// check the current rust-version. As the `dep:` syntax is only avaliable
482+
/// starting with Rust 1.60.0
483+
///
484+
/// `true` means that the rust-version is None or the rust-version is higher
485+
/// than the version needed.
486+
///
487+
/// Note: Previous versions can only use the implicit feature name.
488+
fn check_rust_version_for_optional_dependency(
489+
rust_version: Option<&RustVersion>,
490+
) -> CargoResult<bool> {
491+
match rust_version {
492+
Some(version) => {
493+
let syntax_support_version = String::from("1.60.0");
494+
Ok(version.to_string().cmp(&syntax_support_version).is_ge())
495+
}
496+
None => Ok(true),
497+
}
498+
}
499+
472500
/// Provide the existing dependency for the target table
473501
///
474502
/// If it doesn't exist but exists in another table, let's use that as most likely users

src/cargo/util/toml_mut/dependency.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,13 @@ impl Dependency {
583583
unreachable!("Invalid dependency type: {}", item.type_name());
584584
}
585585
}
586+
587+
/// Convert dependency to Feature.
588+
pub fn to_feature(&self) -> Feature {
589+
let feature = Feature::new(self.name.to_string(), self.rename.to_owned());
590+
let feature_value = feature.as_feature();
591+
feature.add_feature(feature_value)
592+
}
586593
}
587594

588595
fn overwrite_value(
@@ -921,6 +928,76 @@ impl Display for WorkspaceSource {
921928
}
922929
}
923930

931+
pub struct Feature {
932+
/// The name of the feature (as it is set in its `Cargo.toml`).
933+
pub name: String,
934+
/// List of features to add.
935+
pub features: IndexSet<String>,
936+
/// this is the new name for the dependency
937+
/// as a string. None if it is not renamed.
938+
pub rename: Option<String>,
939+
}
940+
941+
impl Feature {
942+
pub fn new(name: String, rename: Option<String>) -> Self {
943+
Self {
944+
name: name,
945+
features: IndexSet::new(),
946+
rename: rename,
947+
}
948+
}
949+
950+
pub fn add_feature(mut self, value: String) -> Self {
951+
self.features.insert(value);
952+
self
953+
}
954+
955+
pub fn as_feature(&self) -> String {
956+
if let Some(rename) = &self.rename {
957+
format!("dep:{}", rename)
958+
} else {
959+
format!("dep:{}", self.name)
960+
}
961+
}
962+
963+
/// Convert feature to TOML.
964+
/// Panics if the path is relative
965+
pub fn to_toml(&self, crate_root: &Path) -> toml_edit::Item {
966+
assert!(
967+
crate_root.is_absolute(),
968+
"Absolute path needed, got: {}",
969+
crate_root.display()
970+
);
971+
let features: toml_edit::Value = self.features.iter().cloned().collect();
972+
toml_edit::value(features)
973+
}
974+
975+
/// Check whether `dep:<dep>` is defined in the features section.
976+
///
977+
/// `true` if there is a define `dep:<dep>`
978+
/// `false` if `dep:<dep>` is not used ever.
979+
///
980+
/// Make sure that `dep:<dep>` is included in one of features in the features table.
981+
pub fn check_duplicate_feature(&self, item: &mut toml_edit::Item) -> bool {
982+
item.as_table_like_mut().unwrap().iter().any(|(_, values)| {
983+
if let Some(values) = &values.as_array() {
984+
values.iter().any(|value| match value.as_str() {
985+
Some(val) => val.to_string().eq(&self.as_feature()),
986+
None => false,
987+
})
988+
} else {
989+
false
990+
}
991+
})
992+
}
993+
}
994+
995+
impl std::fmt::Display for Feature {
996+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
997+
self.name.fmt(f)
998+
}
999+
}
1000+
9241001
#[cfg(test)]
9251002
mod tests {
9261003
use std::path::Path;

src/cargo/util/toml_mut/manifest.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,30 @@ impl LocalManifest {
363363
Ok(())
364364
}
365365

366+
/// Add feature entry to a Cargo.toml.
367+
pub fn insert_into_feature_table(
368+
&mut self,
369+
table_path: &[String],
370+
dep: &Dependency,
371+
) -> CargoResult<()> {
372+
let crate_root = self
373+
.path
374+
.parent()
375+
.expect("manifest path is absolute")
376+
.to_owned();
377+
let dep_key = dep.toml_key();
378+
let feature = dep.to_feature();
379+
let table = self.get_table_mut(table_path)?;
380+
381+
// Check whether `dep:<dep>` is defined in the [features] section.
382+
if !feature.check_duplicate_feature(table) {
383+
let new_feature = feature.to_toml(&crate_root);
384+
table[dep_key] = new_feature;
385+
}
386+
387+
Ok(())
388+
}
389+
366390
/// Remove entry from a Cargo.toml.
367391
pub fn remove_from_table(&mut self, table_path: &[String], name: &str) -> CargoResult<()> {
368392
let parent_table = self.get_table_mut(table_path)?;

tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
some-package = { package = "my-package2", version = "99999.0.0", optional = true }
9+
10+
[features]
11+
some-package = ["dep:some-package"]

tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ version = "0.0.0"
44

55
[dependencies]
66
foo = { workspace = true, optional = true }
7+
8+
[features]
9+
foo = ["dep:foo"]

tests/testsuite/cargo_add/optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]

tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { optional = true, path = "../dependency", version = "0.0.0" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ version = "0.0.0"
44

55
[dependencies]
66
foo = { workspace = true, optional = true }
7+
8+
[features]
9+
foo = ["dep:foo"]

tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ version = "0.0.0"
77

88
[dependencies]
99
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
10+
11+
[features]
12+
your-face = ["dep:your-face"]

tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]

tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]

tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ version = "0.0.0"
77

88
[dependencies]
99
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
10+
11+
[features]
12+
your-face = ["dep:your-face"]

tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { optional = true, version = "20.0" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
a1 = { package = "versioned-package", version = "0.1.1", optional = true }
9+
10+
[features]
11+
a1 = ["dep:a1"]

tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
versioned-package = { version = "0.3.0", optional = true, git = "[ROOTURL]/versioned-package" }
9+
10+
[features]
11+
versioned-package = ["dep:versioned-package"]

tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { version = "0.0.0", optional = true, path = "../dependency" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

0 commit comments

Comments
 (0)