Skip to content

Commit d0bf960

Browse files
committed
Allow running cargo add foo when a foo.workspace = true already exists
1 parent c85a71e commit d0bf960

File tree

33 files changed

+387
-61
lines changed

33 files changed

+387
-61
lines changed

src/cargo/ops/cargo_add/dependency.rs

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

45
use indexmap::IndexSet;
@@ -74,6 +75,7 @@ impl Dependency {
7475
Some(Source::Git(git)) => {
7576
git.version = None;
7677
}
78+
Some(Source::Workspace(_workspace)) => {}
7779
None => {}
7880
}
7981
self
@@ -161,6 +163,7 @@ impl Dependency {
161163
Source::Registry(src) => Some(src.version.as_str()),
162164
Source::Path(src) => src.version.as_deref(),
163165
Source::Git(src) => src.version.as_deref(),
166+
Source::Workspace(_) => None,
164167
}
165168
}
166169

@@ -185,29 +188,47 @@ impl Dependency {
185188
}
186189

187190
/// Get the SourceID for this dependency
188-
pub fn source_id(&self, config: &Config) -> CargoResult<SourceId> {
191+
pub fn source_id(&self, config: &Config) -> CargoResult<MaybeWorkspace<SourceId>> {
189192
match &self.source.as_ref() {
190193
Some(Source::Registry(_)) | None => {
191194
if let Some(r) = self.registry() {
192195
let source_id = SourceId::alt_registry(config, r)?;
193-
Ok(source_id)
196+
Ok(MaybeWorkspace::Other(source_id))
194197
} else {
195198
let source_id = SourceId::crates_io(config)?;
196-
Ok(source_id)
199+
Ok(MaybeWorkspace::Other(source_id))
197200
}
198201
}
199-
Some(Source::Path(source)) => source.source_id(),
200-
Some(Source::Git(source)) => source.source_id(),
202+
Some(Source::Path(source)) => Ok(MaybeWorkspace::Other(source.source_id()?)),
203+
Some(Source::Git(source)) => Ok(MaybeWorkspace::Other(source.source_id()?)),
204+
Some(Source::Workspace(workspace)) => Ok(MaybeWorkspace::Workspace(workspace.clone())),
201205
}
202206
}
203207

204208
/// Query to find this dependency
205-
pub fn query(&self, config: &Config) -> CargoResult<crate::core::dependency::Dependency> {
209+
pub fn query(
210+
&self,
211+
config: &Config,
212+
) -> CargoResult<MaybeWorkspace<crate::core::dependency::Dependency>> {
206213
let source_id = self.source_id(config)?;
207-
crate::core::dependency::Dependency::parse(self.name.as_str(), self.version(), source_id)
214+
match source_id {
215+
MaybeWorkspace::Workspace(workspace) => Ok(MaybeWorkspace::Workspace(workspace)),
216+
MaybeWorkspace::Other(source_id) => Ok(MaybeWorkspace::Other(
217+
crate::core::dependency::Dependency::parse(
218+
self.name.as_str(),
219+
self.version(),
220+
source_id,
221+
)?,
222+
)),
223+
}
208224
}
209225
}
210226

227+
pub enum MaybeWorkspace<T> {
228+
Workspace(WorkspaceSource),
229+
Other(T),
230+
}
231+
211232
impl Dependency {
212233
/// Create a dependency from a TOML table entry
213234
pub fn from_toml(crate_root: &Path, key: &str, item: &toml_edit::Item) -> CargoResult<Self> {
@@ -271,6 +292,15 @@ impl Dependency {
271292
invalid_type(key, "version", version.type_name(), "string")
272293
})?);
273294
src.into()
295+
} else if let Some(workspace) = table.get("workspace") {
296+
let workspace_bool = workspace.as_bool().ok_or_else(|| {
297+
invalid_type(key, "workspace", workspace.type_name(), "bool")
298+
})?;
299+
if !workspace_bool {
300+
anyhow::bail!("`{key}.workspace = false` is unsupported")
301+
}
302+
let src = WorkspaceSource::new();
303+
src.into()
274304
} else {
275305
anyhow::bail!("Unrecognized dependency source for `{key}`");
276306
};
@@ -367,6 +397,12 @@ impl Dependency {
367397
None,
368398
None,
369399
) => toml_edit::value(v),
400+
(false, None, true, Some(Source::Workspace(WorkspaceSource {})), None, None) => {
401+
let mut table = toml_edit::InlineTable::default();
402+
table.set_dotted(true);
403+
table.insert("workspace", true.into());
404+
toml_edit::value(toml_edit::Value::InlineTable(table))
405+
}
370406
// Other cases are represented as an inline table
371407
(_, _, _, _, _, _) => {
372408
let mut table = toml_edit::InlineTable::default();
@@ -397,6 +433,9 @@ impl Dependency {
397433
table.insert("version", r.into());
398434
}
399435
}
436+
Some(Source::Workspace(_)) => {
437+
table.insert("workspace", true.into());
438+
}
400439
None => {}
401440
}
402441
if table.contains_key("version") {
@@ -436,7 +475,7 @@ impl Dependency {
436475
Some(Source::Registry(src)) => {
437476
table.insert("version", toml_edit::value(src.version.as_str()));
438477

439-
for key in ["path", "git", "branch", "tag", "rev"] {
478+
for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
440479
table.remove(key);
441480
}
442481
}
@@ -449,7 +488,7 @@ impl Dependency {
449488
table.remove("version");
450489
}
451490

452-
for key in ["git", "branch", "tag", "rev"] {
491+
for key in ["git", "branch", "tag", "rev", "workspace"] {
453492
table.remove(key);
454493
}
455494
}
@@ -476,7 +515,23 @@ impl Dependency {
476515
table.remove("version");
477516
}
478517

479-
for key in ["path"] {
518+
for key in ["path", "workspace"] {
519+
table.remove(key);
520+
}
521+
}
522+
Some(Source::Workspace(_)) => {
523+
table.set_dotted(true);
524+
for key in [
525+
"version",
526+
"registry",
527+
"registry-index",
528+
"path",
529+
"git",
530+
"branch",
531+
"tag",
532+
"rev",
533+
"package",
534+
] {
480535
table.remove(key);
481536
}
482537
}
@@ -516,12 +571,14 @@ impl Dependency {
516571
.unwrap_or_default();
517572
features.extend(new_features.iter().map(|s| s.as_str()));
518573
let features = toml_edit::value(features.into_iter().collect::<toml_edit::Value>());
574+
table.set_dotted(false);
519575
table.insert("features", features);
520576
} else {
521577
table.remove("features");
522578
}
523579
match self.optional {
524580
Some(v) => {
581+
table.set_dotted(false);
525582
table.insert("optional", toml_edit::value(v));
526583
}
527584
None => {
@@ -596,6 +653,8 @@ pub enum Source {
596653
Path(PathSource),
597654
/// Dependency from a git repo
598655
Git(GitSource),
656+
/// Dependency from a workspace
657+
Workspace(WorkspaceSource),
599658
}
600659

601660
impl Source {
@@ -624,6 +683,15 @@ impl Source {
624683
_ => None,
625684
}
626685
}
686+
687+
/// Access the workspace source, if present
688+
#[allow(dead_code)]
689+
pub fn as_workspace(&self) -> Option<&WorkspaceSource> {
690+
match self {
691+
Self::Workspace(src) => Some(src),
692+
_ => None,
693+
}
694+
}
627695
}
628696

629697
impl std::fmt::Display for Source {
@@ -632,6 +700,7 @@ impl std::fmt::Display for Source {
632700
Self::Registry(src) => src.fmt(f),
633701
Self::Path(src) => src.fmt(f),
634702
Self::Git(src) => src.fmt(f),
703+
Self::Workspace(src) => src.fmt(f),
635704
}
636705
}
637706
}
@@ -660,6 +729,12 @@ impl From<GitSource> for Source {
660729
}
661730
}
662731

732+
impl From<WorkspaceSource> for Source {
733+
fn from(inner: WorkspaceSource) -> Self {
734+
Self::Workspace(inner)
735+
}
736+
}
737+
663738
/// Dependency from a registry
664739
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
665740
#[non_exhaustive]
@@ -822,6 +897,23 @@ impl std::fmt::Display for GitSource {
822897
}
823898
}
824899

900+
/// Dependency from a workspace
901+
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
902+
#[non_exhaustive]
903+
pub struct WorkspaceSource;
904+
905+
impl WorkspaceSource {
906+
pub fn new() -> Self {
907+
Self
908+
}
909+
}
910+
911+
impl Display for WorkspaceSource {
912+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
913+
"workspace".fmt(f)
914+
}
915+
}
916+
825917
#[cfg(test)]
826918
mod tests {
827919
use std::path::Path;

0 commit comments

Comments
 (0)