Skip to content

Commit caca7f2

Browse files
committed
Auto merge of #10606 - Muscraft:cargo-add-support, r=epage
Cargo add support for workspace inheritance Tracking issue: #8415 RFC: rust-lang/rfcs#2906 This PR adds all the required support for workspace inheritance within `cargo-add`. It was split up across a few different PRs as it required `snapbox` support from #10581 and a new `toml_edit` version from #10603. `@epage` and I decided to go ahead with this PR and add in some of the changes those PRs made. `@epage's` name on the commits is from helping to rewrite commits and some very minor additions. Changes: - #10585 - Muscraft#1 - Muscraft#3 - Muscraft#2 - Muscraft#4 r? `@epage`
2 parents 5600004 + aa7d116 commit caca7f2

137 files changed

Lines changed: 1149 additions & 79 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/cargo/ops/cargo_add/dependency.rs

Lines changed: 122 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::collections::BTreeMap;
2+
use std::fmt::{Display, Formatter};
23
use std::path::{Path, PathBuf};
34

45
use indexmap::IndexSet;
6+
use toml_edit::KeyMut;
57

68
use super::manifest::str_or_1_len_table;
79
use crate::core::FeatureMap;
@@ -27,6 +29,8 @@ pub struct Dependency {
2729
pub features: Option<IndexSet<String>>,
2830
/// Whether default features are enabled
2931
pub default_features: Option<bool>,
32+
/// List of features inherited from a workspace dependency
33+
pub inherited_features: Option<IndexSet<String>>,
3034

3135
/// Where the dependency comes from
3236
pub source: Option<Source>,
@@ -49,6 +53,7 @@ impl Dependency {
4953
optional: None,
5054
features: None,
5155
default_features: None,
56+
inherited_features: None,
5257
source: None,
5358
registry: None,
5459
rename: None,
@@ -74,6 +79,7 @@ impl Dependency {
7479
Some(Source::Git(git)) => {
7580
git.version = None;
7681
}
82+
Some(Source::Workspace(_workspace)) => {}
7783
None => {}
7884
}
7985
self
@@ -150,6 +156,12 @@ impl Dependency {
150156
self
151157
}
152158

159+
/// Set features as an array of string (does some basic parsing)
160+
pub fn set_inherited_features(mut self, features: IndexSet<String>) -> Self {
161+
self.inherited_features = Some(features);
162+
self
163+
}
164+
153165
/// Get the dependency source
154166
pub fn source(&self) -> Option<&Source> {
155167
self.source.as_ref()
@@ -161,6 +173,7 @@ impl Dependency {
161173
Source::Registry(src) => Some(src.version.as_str()),
162174
Source::Path(src) => src.version.as_deref(),
163175
Source::Git(src) => src.version.as_deref(),
176+
Source::Workspace(_) => None,
164177
}
165178
}
166179

@@ -185,29 +198,47 @@ impl Dependency {
185198
}
186199

187200
/// Get the SourceID for this dependency
188-
pub fn source_id(&self, config: &Config) -> CargoResult<SourceId> {
201+
pub fn source_id(&self, config: &Config) -> CargoResult<MaybeWorkspace<SourceId>> {
189202
match &self.source.as_ref() {
190203
Some(Source::Registry(_)) | None => {
191204
if let Some(r) = self.registry() {
192205
let source_id = SourceId::alt_registry(config, r)?;
193-
Ok(source_id)
206+
Ok(MaybeWorkspace::Other(source_id))
194207
} else {
195208
let source_id = SourceId::crates_io(config)?;
196-
Ok(source_id)
209+
Ok(MaybeWorkspace::Other(source_id))
197210
}
198211
}
199-
Some(Source::Path(source)) => source.source_id(),
200-
Some(Source::Git(source)) => source.source_id(),
212+
Some(Source::Path(source)) => Ok(MaybeWorkspace::Other(source.source_id()?)),
213+
Some(Source::Git(source)) => Ok(MaybeWorkspace::Other(source.source_id()?)),
214+
Some(Source::Workspace(workspace)) => Ok(MaybeWorkspace::Workspace(workspace.clone())),
201215
}
202216
}
203217

204218
/// Query to find this dependency
205-
pub fn query(&self, config: &Config) -> CargoResult<crate::core::dependency::Dependency> {
219+
pub fn query(
220+
&self,
221+
config: &Config,
222+
) -> CargoResult<MaybeWorkspace<crate::core::dependency::Dependency>> {
206223
let source_id = self.source_id(config)?;
207-
crate::core::dependency::Dependency::parse(self.name.as_str(), self.version(), source_id)
224+
match source_id {
225+
MaybeWorkspace::Workspace(workspace) => Ok(MaybeWorkspace::Workspace(workspace)),
226+
MaybeWorkspace::Other(source_id) => Ok(MaybeWorkspace::Other(
227+
crate::core::dependency::Dependency::parse(
228+
self.name.as_str(),
229+
self.version(),
230+
source_id,
231+
)?,
232+
)),
233+
}
208234
}
209235
}
210236

237+
pub enum MaybeWorkspace<T> {
238+
Workspace(WorkspaceSource),
239+
Other(T),
240+
}
241+
211242
impl Dependency {
212243
/// Create a dependency from a TOML table entry
213244
pub fn from_toml(crate_root: &Path, key: &str, item: &toml_edit::Item) -> CargoResult<Self> {
@@ -271,6 +302,15 @@ impl Dependency {
271302
invalid_type(key, "version", version.type_name(), "string")
272303
})?);
273304
src.into()
305+
} else if let Some(workspace) = table.get("workspace") {
306+
let workspace_bool = workspace.as_bool().ok_or_else(|| {
307+
invalid_type(key, "workspace", workspace.type_name(), "bool")
308+
})?;
309+
if !workspace_bool {
310+
anyhow::bail!("`{key}.workspace = false` is unsupported")
311+
}
312+
let src = WorkspaceSource::new();
313+
src.into()
274314
} else {
275315
anyhow::bail!("Unrecognized dependency source for `{key}`");
276316
};
@@ -320,6 +360,7 @@ impl Dependency {
320360
features,
321361
available_features,
322362
optional,
363+
inherited_features: None,
323364
};
324365
Ok(dep)
325366
} else {
@@ -367,6 +408,12 @@ impl Dependency {
367408
None,
368409
None,
369410
) => toml_edit::value(v),
411+
(false, None, true, Some(Source::Workspace(WorkspaceSource {})), None, None) => {
412+
let mut table = toml_edit::InlineTable::default();
413+
table.set_dotted(true);
414+
table.insert("workspace", true.into());
415+
toml_edit::value(toml_edit::Value::InlineTable(table))
416+
}
370417
// Other cases are represented as an inline table
371418
(_, _, _, _, _, _) => {
372419
let mut table = toml_edit::InlineTable::default();
@@ -397,6 +444,9 @@ impl Dependency {
397444
table.insert("version", r.into());
398445
}
399446
}
447+
Some(Source::Workspace(_)) => {
448+
table.insert("workspace", true.into());
449+
}
400450
None => {}
401451
}
402452
if table.contains_key("version") {
@@ -427,16 +477,24 @@ impl Dependency {
427477
}
428478

429479
/// Modify existing entry to match this dependency
430-
pub fn update_toml(&self, crate_root: &Path, item: &mut toml_edit::Item) {
480+
pub fn update_toml<'k>(
481+
&self,
482+
crate_root: &Path,
483+
key: &mut KeyMut<'k>,
484+
item: &mut toml_edit::Item,
485+
) {
431486
if str_or_1_len_table(item) {
432487
// Nothing to preserve
433488
*item = self.to_toml(crate_root);
489+
if self.source != Some(Source::Workspace(WorkspaceSource)) {
490+
key.fmt();
491+
}
434492
} else if let Some(table) = item.as_table_like_mut() {
435493
match &self.source {
436494
Some(Source::Registry(src)) => {
437495
table.insert("version", toml_edit::value(src.version.as_str()));
438496

439-
for key in ["path", "git", "branch", "tag", "rev"] {
497+
for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
440498
table.remove(key);
441499
}
442500
}
@@ -449,7 +507,7 @@ impl Dependency {
449507
table.remove("version");
450508
}
451509

452-
for key in ["git", "branch", "tag", "rev"] {
510+
for key in ["git", "branch", "tag", "rev", "workspace"] {
453511
table.remove(key);
454512
}
455513
}
@@ -476,7 +534,23 @@ impl Dependency {
476534
table.remove("version");
477535
}
478536

479-
for key in ["path"] {
537+
for key in ["path", "workspace"] {
538+
table.remove(key);
539+
}
540+
}
541+
Some(Source::Workspace(_)) => {
542+
table.set_dotted(true);
543+
for key in [
544+
"version",
545+
"registry",
546+
"registry-index",
547+
"path",
548+
"git",
549+
"branch",
550+
"tag",
551+
"rev",
552+
"package",
553+
] {
480554
table.remove(key);
481555
}
482556
}
@@ -516,12 +590,14 @@ impl Dependency {
516590
.unwrap_or_default();
517591
features.extend(new_features.iter().map(|s| s.as_str()));
518592
let features = toml_edit::value(features.into_iter().collect::<toml_edit::Value>());
593+
table.set_dotted(false);
519594
table.insert("features", features);
520595
} else {
521596
table.remove("features");
522597
}
523598
match self.optional {
524599
Some(v) => {
600+
table.set_dotted(false);
525601
table.insert("optional", toml_edit::value(v));
526602
}
527603
None => {
@@ -596,6 +672,8 @@ pub enum Source {
596672
Path(PathSource),
597673
/// Dependency from a git repo
598674
Git(GitSource),
675+
/// Dependency from a workspace
676+
Workspace(WorkspaceSource),
599677
}
600678

601679
impl Source {
@@ -624,6 +702,15 @@ impl Source {
624702
_ => None,
625703
}
626704
}
705+
706+
/// Access the workspace source, if present
707+
#[allow(dead_code)]
708+
pub fn as_workspace(&self) -> Option<&WorkspaceSource> {
709+
match self {
710+
Self::Workspace(src) => Some(src),
711+
_ => None,
712+
}
713+
}
627714
}
628715

629716
impl std::fmt::Display for Source {
@@ -632,6 +719,7 @@ impl std::fmt::Display for Source {
632719
Self::Registry(src) => src.fmt(f),
633720
Self::Path(src) => src.fmt(f),
634721
Self::Git(src) => src.fmt(f),
722+
Self::Workspace(src) => src.fmt(f),
635723
}
636724
}
637725
}
@@ -660,6 +748,12 @@ impl From<GitSource> for Source {
660748
}
661749
}
662750

751+
impl From<WorkspaceSource> for Source {
752+
fn from(inner: WorkspaceSource) -> Self {
753+
Self::Workspace(inner)
754+
}
755+
}
756+
663757
/// Dependency from a registry
664758
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
665759
#[non_exhaustive]
@@ -822,6 +916,23 @@ impl std::fmt::Display for GitSource {
822916
}
823917
}
824918

919+
/// Dependency from a workspace
920+
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
921+
#[non_exhaustive]
922+
pub struct WorkspaceSource;
923+
924+
impl WorkspaceSource {
925+
pub fn new() -> Self {
926+
Self
927+
}
928+
}
929+
930+
impl Display for WorkspaceSource {
931+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
932+
"workspace".fmt(f)
933+
}
934+
}
935+
825936
#[cfg(test)]
826937
mod tests {
827938
use std::path::Path;

src/cargo/ops/cargo_add/manifest.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,12 @@ impl LocalManifest {
349349
let dep_key = dep.toml_key();
350350

351351
let table = self.get_table_mut(table_path)?;
352-
if let Some(dep_item) = table.as_table_like_mut().unwrap().get_mut(dep_key) {
353-
dep.update_toml(&crate_root, dep_item);
352+
if let Some((mut dep_key, dep_item)) = table
353+
.as_table_like_mut()
354+
.unwrap()
355+
.get_key_value_mut(dep_key)
356+
{
357+
dep.update_toml(&crate_root, &mut dep_key, dep_item);
354358
} else {
355359
let new_dependency = dep.to_toml(&crate_root);
356360
table[dep_key] = new_dependency;

0 commit comments

Comments
 (0)