Skip to content

Commit

Permalink
Implement path-bases (RFC 3529) 2/n: cargo add support
Browse files Browse the repository at this point in the history
RFC: rust-lang/rfcs#3529
Tracking Issue: #14355

This PR adds the `--base` option to `cargo add` to allow adding a path dependency with a path base.
  • Loading branch information
dpaoliello committed Aug 27, 2024
1 parent d46eab0 commit df56212
Show file tree
Hide file tree
Showing 46 changed files with 566 additions and 52 deletions.
8 changes: 8 additions & 0 deletions src/bin/cargo/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ Example uses:
.help("Filesystem path to local crate to add")
.group("selected")
.conflicts_with("git"),
clap::Arg::new("base")
.long("base")
.action(ArgAction::Set)
.value_name("BASE")
.help("The path base to use when adding from a local crate (unstable).")
.requires("path"),
clap::Arg::new("git")
.long("git")
.action(ArgAction::Set)
Expand Down Expand Up @@ -224,6 +230,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {

fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult<Vec<DepOp>> {
let path = matches.get_one::<String>("path");
let base = matches.get_one::<String>("base");
let git = matches.get_one::<String>("git");
let branch = matches.get_one::<String>("branch");
let rev = matches.get_one::<String>("rev");
Expand Down Expand Up @@ -329,6 +336,7 @@ fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult
public,
registry: registry.clone(),
path: path.map(String::from),
base: base.map(String::from),
git: git.map(String::from),
branch: branch.map(String::from),
rev: rev.map(String::from),
Expand Down
37 changes: 29 additions & 8 deletions src/cargo/ops/cargo_add/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::str::FromStr;
use anyhow::Context as _;
use cargo_util::paths;
use cargo_util_schemas::core::PartialVersion;
use cargo_util_schemas::manifest::PathBaseName;
use cargo_util_schemas::manifest::RustVersion;
use indexmap::IndexSet;
use itertools::Itertools;
Expand All @@ -28,6 +29,7 @@ use crate::core::Workspace;
use crate::sources::source::QueryKind;
use crate::util::cache_lock::CacheLockMode;
use crate::util::style;
use crate::util::toml::lookup_path_base;
use crate::util::toml_mut::dependency::Dependency;
use crate::util::toml_mut::dependency::GitSource;
use crate::util::toml_mut::dependency::MaybeWorkspace;
Expand Down Expand Up @@ -270,8 +272,11 @@ pub struct DepOp {
/// Registry for looking up dependency version
pub registry: Option<String>,

/// Git repo for dependency
/// File system path for dependency
pub path: Option<String>,
/// Specify a named base for a path dependency
pub base: Option<String>,

/// Git repo for dependency
pub git: Option<String>,
/// Specify an alternative git branch
Expand Down Expand Up @@ -331,10 +336,22 @@ fn resolve_dependency(
};
selected
} else if let Some(raw_path) = &arg.path {
let base_name_and_value = if let Some(base_name) = &arg.base {
let workspace_root = || Ok(ws.root_manifest().parent().unwrap());
let base_value = lookup_path_base(
&PathBaseName::new(base_name.clone())?,
gctx,
&workspace_root,
spec.manifest().unstable_features(),
)?;
Some((base_name.clone(), base_value))
} else {
None
};
let path = paths::normalize_path(&std::env::current_dir()?.join(raw_path));
let src = PathSource::new(&path);
let src = PathSource::new_with_base(path, base_name_and_value);

let selected = if let Some(crate_spec) = &crate_spec {
if let Some(crate_spec) = &crate_spec {
if let Some(v) = crate_spec.version_req() {
// crate specifier includes a version (e.g. `[email protected]`)
anyhow::bail!("cannot specify a path (`{raw_path}`) with a version (`{v}`).");
Expand All @@ -349,11 +366,10 @@ fn resolve_dependency(
}
selected
} else {
let mut source = crate::sources::PathSource::new(&path, src.source_id()?, gctx);
let mut source = crate::sources::PathSource::new(&src.path, src.source_id()?, gctx);
let package = source.root_package()?;
Dependency::from(package.summary())
};
selected
Dependency::from_summary(package.summary(), &src.base_name_and_value)
}
} else if let Some(crate_spec) = &crate_spec {
crate_spec.to_dependency()?
} else {
Expand Down Expand Up @@ -733,7 +749,12 @@ fn select_package(
anyhow::bail!("the crate `{dependency}` could not be found at `{source}`")
}
1 => {
let mut dep = Dependency::from(&possibilities[0]);
let base_name_and_value = dependency
.source()
.map(|s| s.as_path().map(|p| &p.base_name_and_value))
.flatten()
.unwrap_or(&None);
let mut dep = Dependency::from_summary(&possibilities[0], base_name_and_value);
if let Some(reg_name) = dependency.registry.as_deref() {
dep = dep.set_registry(reg_name);
}
Expand Down
58 changes: 48 additions & 10 deletions src/cargo/util/toml_mut/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,15 @@ impl Dependency {
table.insert("version", src.version.as_str().into());
}
Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path);
if let Some(r) = src.version.as_deref() {
table.insert("version", r.into());
}
table.insert("path", relpath.into());
if let Some((base_name, base_value)) = src.base_name_and_value.as_ref() {
table.insert("base", base_name.into());
table.insert("path", path_field(base_value, &src.path).into());
} else {
table.insert("path", path_field(crate_root, &src.path).into());
}
}
Some(Source::Git(src)) => {
table.insert("git", src.git.as_str().into());
Expand Down Expand Up @@ -493,13 +497,18 @@ impl Dependency {
Some(Source::Registry(src)) => {
overwrite_value(table, "version", src.version.as_str());

for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
for key in ["path", "git", "branch", "tag", "rev", "workspace", "base"] {
table.remove(key);
}
}
Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path);
overwrite_value(table, "path", relpath);
if let Some((base_name, base_value)) = src.base_name_and_value.as_ref() {
overwrite_value(table, "base", base_name);
overwrite_value(table, "path", path_field(base_value, &src.path));
} else {
table.remove("base");
overwrite_value(table, "path", path_field(crate_root, &src.path));
}
if let Some(r) = src.version.as_deref() {
overwrite_value(table, "version", r);
} else {
Expand Down Expand Up @@ -533,7 +542,7 @@ impl Dependency {
table.remove("version");
}

for key in ["path", "workspace"] {
for key in ["path", "workspace", "base"] {
table.remove(key);
}
}
Expand All @@ -552,6 +561,7 @@ impl Dependency {
"rev",
"package",
"default-features",
"base",
] {
table.remove(key);
}
Expand Down Expand Up @@ -653,10 +663,13 @@ impl std::fmt::Display for Dependency {
}
}

impl<'s> From<&'s Summary> for Dependency {
fn from(other: &'s Summary) -> Self {
impl Dependency {
pub(crate) fn from_summary(
other: &Summary,
base_name_and_value: &Option<(String, PathBuf)>,
) -> Self {
let source: Source = if let Some(path) = other.source_id().local_path() {
PathSource::new(path)
PathSource::new_with_base(path, base_name_and_value.clone())
.set_version(other.version().to_string())
.into()
} else if let Some(git_ref) = other.source_id().git_reference() {
Expand All @@ -676,6 +689,12 @@ impl<'s> From<&'s Summary> for Dependency {
}
}

impl<'s> From<&'s Summary> for Dependency {
fn from(other: &'s Summary) -> Self {
Dependency::from_summary(other, &None)
}
}

impl From<Summary> for Dependency {
fn from(other: Summary) -> Self {
(&other).into()
Expand Down Expand Up @@ -812,6 +831,8 @@ impl std::fmt::Display for RegistrySource {
pub struct PathSource {
/// Local, absolute path.
pub path: PathBuf,
/// If using a named base, the base and relative path.
pub base_name_and_value: Option<(String, PathBuf)>,
/// Version requirement for when published.
pub version: Option<String>,
}
Expand All @@ -821,6 +842,19 @@ impl PathSource {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
base_name_and_value: None,
version: None,
}
}

/// Specify dependency from a path and an optional base.
pub fn new_with_base(
path: impl Into<PathBuf>,
base_name_and_value: Option<(String, PathBuf)>,
) -> Self {
Self {
path: path.into(),
base_name_and_value,
version: None,
}
}
Expand All @@ -843,7 +877,11 @@ impl PathSource {

impl std::fmt::Display for PathSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.path.display().fmt(f)
if let Some((base_name, _)) = &self.base_name_and_value {
write!(f, "{{{base_name}}}/{}", self.path.display())
} else {
self.path.display().fmt(f)
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/doc/man/cargo-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ Specific commit to use when adding from git.
[Filesystem path](../reference/specifying-dependencies.html#specifying-path-dependencies) to local crate to add.
{{/option}}

{{#option "`--base` _base_" }}
The [path base](../reference/unstable.html#path-bases) to use when adding a local crate.

[Unstable (nightly-only)](../reference/unstable.html#path-bases)
{{/option}}

{{> options-registry }}

{{/options}}
Expand Down
8 changes: 8 additions & 0 deletions src/doc/man/generated_txt/cargo-add.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies>
to local crate to add.

--base base
The path base
<https://doc.rust-lang.org/cargo/reference/unstable.html#path-bases>
to use when adding a local crate.

Unstable (nightly-only)
<https://doc.rust-lang.org/cargo/reference/unstable.html#path-bases>

--registry registry
Name of the registry to use. Registry names are defined in Cargo
config files
Expand Down
5 changes: 5 additions & 0 deletions src/doc/src/commands/cargo-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ dependency will be listed in the command's output.
<dd class="option-desc"><a href="../reference/specifying-dependencies.html#specifying-path-dependencies">Filesystem path</a> to local crate to add.</dd>


<dt class="option-term" id="option-cargo-add---base"><a class="option-anchor" href="#option-cargo-add---base"></a><code>--base</code> <em>base</em></dt>
<dd class="option-desc">The <a href="../reference/unstable.html#path-bases">path base</a> to use when adding a local crate.</p>
<p><a href="../reference/unstable.html#path-bases">Unstable (nightly-only)</a></dd>


<dt class="option-term" id="option-cargo-add---registry"><a class="option-anchor" href="#option-cargo-add---registry"></a><code>--registry</code> <em>registry</em></dt>
<dd class="option-desc">Name of the registry to use. Registry names are defined in <a href="../reference/config.html">Cargo config
files</a>. If not specified, the default registry is used,
Expand Down
7 changes: 7 additions & 0 deletions src/etc/man/cargo-add.1
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ Specific commit to use when adding from git.
\fIFilesystem path\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.html#specifying\-path\-dependencies> to local crate to add.
.RE
.sp
\fB\-\-base\fR \fIbase\fR
.RS 4
The \fIpath base\fR <https://doc.rust\-lang.org/cargo/reference/unstable.html#path\-bases> to use when adding a local crate.
.sp
\fIUnstable (nightly\-only)\fR <https://doc.rust\-lang.org/cargo/reference/unstable.html#path\-bases>
.RE
.sp
\fB\-\-registry\fR \fIregistry\fR
.RS 4
Name of the registry to use. Registry names are defined in \fICargo config
Expand Down
Loading

0 comments on commit df56212

Please sign in to comment.