Implement path-bases (RFC 3529) 2/n: cargo add support

RFC: https://github.com/rust-lang/rfcs/pull/3529
Tracking Issue: https://github.com/rust-lang/cargo/issues/14355

This PR adds the `--base` option to `cargo add` to allow adding a path dependency with a path base.
This commit is contained in:
Daniel Paoliello 2024-01-03 14:24:11 -08:00
parent e86545014c
commit 8de55540f0
69 changed files with 1008 additions and 110 deletions

View File

@ -101,6 +101,12 @@ Example uses:
.help("Filesystem path to local crate to add") .help("Filesystem path to local crate to add")
.group("selected") .group("selected")
.conflicts_with("git"), .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") clap::Arg::new("git")
.long("git") .long("git")
.action(ArgAction::Set) .action(ArgAction::Set)
@ -224,6 +230,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult<Vec<DepOp>> { fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult<Vec<DepOp>> {
let path = matches.get_one::<String>("path"); let path = matches.get_one::<String>("path");
let base = matches.get_one::<String>("base");
let git = matches.get_one::<String>("git"); let git = matches.get_one::<String>("git");
let branch = matches.get_one::<String>("branch"); let branch = matches.get_one::<String>("branch");
let rev = matches.get_one::<String>("rev"); let rev = matches.get_one::<String>("rev");
@ -329,6 +336,7 @@ fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult
public, public,
registry: registry.clone(), registry: registry.clone(),
path: path.map(String::from), path: path.map(String::from),
base: base.map(String::from),
git: git.map(String::from), git: git.map(String::from),
branch: branch.map(String::from), branch: branch.map(String::from),
rev: rev.map(String::from), rev: rev.map(String::from),

View File

@ -167,20 +167,37 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
let members = workspace let members = workspace
.members() .members()
.map(|p| LocalManifest::try_new(p.manifest_path())) .map(|p| {
Ok((
LocalManifest::try_new(p.manifest_path())?,
p.manifest().unstable_features(),
))
})
.collect::<CargoResult<Vec<_>>>()?; .collect::<CargoResult<Vec<_>>>()?;
let mut dependencies = members let mut dependencies = members
.iter() .into_iter()
.flat_map(|manifest| { .flat_map(|(manifest, unstable_features)| {
manifest.get_sections().into_iter().flat_map(|(_, table)| { manifest
table .get_sections()
.as_table_like() .into_iter()
.unwrap() .flat_map(move |(_, table)| {
.iter() table
.map(|(key, item)| Dependency::from_toml(&manifest.path, key, item)) .as_table_like()
.collect::<Vec<_>>() .unwrap()
}) .iter()
.map(|(key, item)| {
Dependency::from_toml(
workspace.gctx(),
workspace.root(),
&manifest.path,
&unstable_features,
key,
item,
)
})
.collect::<Vec<_>>()
})
}) })
.collect::<CargoResult<Vec<_>>>()?; .collect::<CargoResult<Vec<_>>>()?;
@ -192,7 +209,14 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
{ {
deps_table.set_implicit(true); deps_table.set_implicit(true);
for (key, item) in deps_table.iter_mut() { for (key, item) in deps_table.iter_mut() {
let ws_dep = Dependency::from_toml(&workspace.root(), key.get(), item)?; let ws_dep = Dependency::from_toml(
workspace.gctx(),
workspace.root(),
&workspace.root(),
workspace.unstable_features(),
key.get(),
item,
)?;
// search for uses of this workspace dependency // search for uses of this workspace dependency
let mut is_used = false; let mut is_used = false;
@ -329,7 +353,14 @@ fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResul
patch_table.set_implicit(true); patch_table.set_implicit(true);
for (key, item) in patch_table.iter_mut() { for (key, item) in patch_table.iter_mut() {
let dep = Dependency::from_toml(&workspace.root_manifest(), key.get(), item)?; let dep = Dependency::from_toml(
workspace.gctx(),
workspace.root(),
&workspace.root_manifest(),
workspace.unstable_features(),
key.get(),
item,
)?;
// Generate a PackageIdSpec url for querying // Generate a PackageIdSpec url for querying
let url = if let MaybeWorkspace::Other(source_id) = let url = if let MaybeWorkspace::Other(source_id) =

View File

@ -12,6 +12,7 @@ use std::str::FromStr;
use anyhow::Context as _; use anyhow::Context as _;
use cargo_util::paths; use cargo_util::paths;
use cargo_util_schemas::core::PartialVersion; use cargo_util_schemas::core::PartialVersion;
use cargo_util_schemas::manifest::PathBaseName;
use cargo_util_schemas::manifest::RustVersion; use cargo_util_schemas::manifest::RustVersion;
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::Itertools; use itertools::Itertools;
@ -20,6 +21,7 @@ use toml_edit::Item as TomlItem;
use crate::core::dependency::DepKind; use crate::core::dependency::DepKind;
use crate::core::registry::PackageRegistry; use crate::core::registry::PackageRegistry;
use crate::core::FeatureValue; use crate::core::FeatureValue;
use crate::core::Features;
use crate::core::Package; use crate::core::Package;
use crate::core::Registry; use crate::core::Registry;
use crate::core::Shell; use crate::core::Shell;
@ -28,6 +30,7 @@ use crate::core::Workspace;
use crate::sources::source::QueryKind; use crate::sources::source::QueryKind;
use crate::util::cache_lock::CacheLockMode; use crate::util::cache_lock::CacheLockMode;
use crate::util::style; use crate::util::style;
use crate::util::toml::lookup_path_base;
use crate::util::toml_mut::dependency::Dependency; use crate::util::toml_mut::dependency::Dependency;
use crate::util::toml_mut::dependency::GitSource; use crate::util::toml_mut::dependency::GitSource;
use crate::util::toml_mut::dependency::MaybeWorkspace; use crate::util::toml_mut::dependency::MaybeWorkspace;
@ -197,7 +200,13 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
print_dep_table_msg(&mut options.gctx.shell(), &dep)?; print_dep_table_msg(&mut options.gctx.shell(), &dep)?;
manifest.insert_into_table(&dep_table, &dep)?; manifest.insert_into_table(
&dep_table,
&dep,
workspace.gctx(),
workspace.root(),
options.spec.manifest().unstable_features(),
)?;
if dep.optional == Some(true) { if dep.optional == Some(true) {
let is_namespaced_features_supported = let is_namespaced_features_supported =
check_rust_version_for_optional_dependency(options.spec.rust_version())?; check_rust_version_for_optional_dependency(options.spec.rust_version())?;
@ -270,8 +279,11 @@ pub struct DepOp {
/// Registry for looking up dependency version /// Registry for looking up dependency version
pub registry: Option<String>, pub registry: Option<String>,
/// Git repo for dependency /// File system path for dependency
pub path: Option<String>, pub path: Option<String>,
/// Specify a named base for a path dependency
pub base: Option<String>,
/// Git repo for dependency /// Git repo for dependency
pub git: Option<String>, pub git: Option<String>,
/// Specify an alternative git branch /// Specify an alternative git branch
@ -332,7 +344,19 @@ fn resolve_dependency(
selected selected
} else if let Some(raw_path) = &arg.path { } else if let Some(raw_path) = &arg.path {
let path = paths::normalize_path(&std::env::current_dir()?.join(raw_path)); let path = paths::normalize_path(&std::env::current_dir()?.join(raw_path));
let src = PathSource::new(&path); let mut src = PathSource::new(path);
src.base = arg.base.clone();
if let Some(base) = &arg.base {
// Validate that the base is valid.
let workspace_root = || Ok(ws.root_manifest().parent().unwrap());
lookup_path_base(
&PathBaseName::new(base.clone())?,
&gctx,
&workspace_root,
spec.manifest().unstable_features(),
)?;
}
let selected = if let Some(crate_spec) = &crate_spec { let selected = if let Some(crate_spec) = &crate_spec {
if let Some(v) = crate_spec.version_req() { if let Some(v) = crate_spec.version_req() {
@ -349,9 +373,13 @@ fn resolve_dependency(
} }
selected selected
} else { } 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()?; let package = source.root_package()?;
Dependency::from(package.summary()) let mut selected = Dependency::from(package.summary());
if let Some(Source::Path(selected_src)) = &mut selected.source {
selected_src.base = src.base;
}
selected
}; };
selected selected
} else if let Some(crate_spec) = &crate_spec { } else if let Some(crate_spec) = &crate_spec {
@ -361,9 +389,16 @@ fn resolve_dependency(
}; };
selected_dep = populate_dependency(selected_dep, arg); selected_dep = populate_dependency(selected_dep, arg);
let lookup = |dep_key: &_| get_existing_dependency(manifest, dep_key, section); let lookup = |dep_key: &_| {
get_existing_dependency(
ws,
spec.manifest().unstable_features(),
manifest,
dep_key,
section,
)
};
let old_dep = fuzzy_lookup(&mut selected_dep, lookup, gctx)?; let old_dep = fuzzy_lookup(&mut selected_dep, lookup, gctx)?;
let mut dependency = if let Some(mut old_dep) = old_dep.clone() { let mut dependency = if let Some(mut old_dep) = old_dep.clone() {
if old_dep.name != selected_dep.name { if old_dep.name != selected_dep.name {
// Assuming most existing keys are not relevant when the package changes // Assuming most existing keys are not relevant when the package changes
@ -385,7 +420,9 @@ fn resolve_dependency(
if dependency.source().is_none() { if dependency.source().is_none() {
// Checking for a workspace dependency happens first since a member could be specified // Checking for a workspace dependency happens first since a member could be specified
// in the workspace dependencies table as a dependency // in the workspace dependencies table as a dependency
let lookup = |toml_key: &_| Ok(find_workspace_dep(toml_key, ws.root_manifest()).ok()); let lookup = |toml_key: &_| {
Ok(find_workspace_dep(toml_key, ws, ws.root_manifest(), ws.unstable_features()).ok())
};
if let Some(_dep) = fuzzy_lookup(&mut dependency, lookup, gctx)? { if let Some(_dep) = fuzzy_lookup(&mut dependency, lookup, gctx)? {
dependency = dependency.set_source(WorkspaceSource::new()); dependency = dependency.set_source(WorkspaceSource::new());
} else if let Some(package) = ws.members().find(|p| p.name().as_str() == dependency.name) { } else if let Some(package) = ws.members().find(|p| p.name().as_str() == dependency.name) {
@ -432,7 +469,12 @@ fn resolve_dependency(
let query = dependency.query(gctx)?; let query = dependency.query(gctx)?;
let query = match query { let query = match query {
MaybeWorkspace::Workspace(_workspace) => { MaybeWorkspace::Workspace(_workspace) => {
let dep = find_workspace_dep(dependency.toml_key(), ws.root_manifest())?; let dep = find_workspace_dep(
dependency.toml_key(),
ws,
ws.root_manifest(),
ws.unstable_features(),
)?;
if let Some(features) = dep.features.clone() { if let Some(features) = dep.features.clone() {
dependency = dependency.set_inherited_features(features); dependency = dependency.set_inherited_features(features);
} }
@ -547,6 +589,8 @@ fn check_rust_version_for_optional_dependency(
/// If it doesn't exist but exists in another table, let's use that as most likely users /// If it doesn't exist but exists in another table, let's use that as most likely users
/// want to use the same version across all tables unless they are renaming. /// want to use the same version across all tables unless they are renaming.
fn get_existing_dependency( fn get_existing_dependency(
ws: &Workspace<'_>,
unstable_features: &Features,
manifest: &LocalManifest, manifest: &LocalManifest,
dep_key: &str, dep_key: &str,
section: &DepTable, section: &DepTable,
@ -561,7 +605,7 @@ fn get_existing_dependency(
} }
let mut possible: Vec<_> = manifest let mut possible: Vec<_> = manifest
.get_dependency_versions(dep_key) .get_dependency_versions(dep_key, ws, unstable_features)
.map(|(path, dep)| { .map(|(path, dep)| {
let key = if path == *section { let key = if path == *section {
(Key::Existing, true) (Key::Existing, true)
@ -776,6 +820,11 @@ fn select_package(
if let Some(reg_name) = dependency.registry.as_deref() { if let Some(reg_name) = dependency.registry.as_deref() {
dep = dep.set_registry(reg_name); dep = dep.set_registry(reg_name);
} }
if let Some(Source::Path(PathSource { base, .. })) = dependency.source() {
if let Some(Source::Path(dep_src)) = &mut dep.source {
dep_src.base = base.clone();
}
}
Ok(dep) Ok(dep)
} }
_ => { _ => {
@ -1107,7 +1156,12 @@ fn format_features_version_suffix(dep: &DependencyUI) -> String {
} }
} }
fn find_workspace_dep(toml_key: &str, root_manifest: &Path) -> CargoResult<Dependency> { fn find_workspace_dep(
toml_key: &str,
ws: &Workspace<'_>,
root_manifest: &Path,
unstable_features: &Features,
) -> CargoResult<Dependency> {
let manifest = LocalManifest::try_new(root_manifest)?; let manifest = LocalManifest::try_new(root_manifest)?;
let manifest = manifest let manifest = manifest
.data .data
@ -1127,7 +1181,14 @@ fn find_workspace_dep(toml_key: &str, root_manifest: &Path) -> CargoResult<Depen
let dep_item = dependencies let dep_item = dependencies
.get(toml_key) .get(toml_key)
.with_context(|| format!("could not find {toml_key} in `workspace.dependencies`"))?; .with_context(|| format!("could not find {toml_key} in `workspace.dependencies`"))?;
Dependency::from_toml(root_manifest.parent().unwrap(), toml_key, dep_item) Dependency::from_toml(
ws.gctx(),
ws.root(),
root_manifest.parent().unwrap(),
unstable_features,
toml_key,
dep_item,
)
} }
/// Convert a `semver::VersionReq` into a rendered `semver::Version` if all fields are fully /// Convert a `semver::VersionReq` into a rendered `semver::Version` if all fields are fully

View File

@ -398,11 +398,16 @@ pub fn write_manifest_upgrades(
let mut any_file_has_changed = false; let mut any_file_has_changed = false;
let manifest_paths = std::iter::once(ws.root_manifest()) let items = std::iter::once((ws.root_manifest(), ws.unstable_features()))
.chain(ws.members().map(|member| member.manifest_path())) .chain(ws.members().map(|member| {
(
member.manifest_path(),
member.manifest().unstable_features(),
)
}))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for manifest_path in manifest_paths { for (manifest_path, unstable_features) in items {
trace!("updating TOML manifest at `{manifest_path:?}` with upgraded dependencies"); trace!("updating TOML manifest at `{manifest_path:?}` with upgraded dependencies");
let crate_root = manifest_path let crate_root = manifest_path
@ -417,7 +422,10 @@ pub fn write_manifest_upgrades(
for (mut dep_key, dep_item) in dep_table.iter_mut() { for (mut dep_key, dep_item) in dep_table.iter_mut() {
let dep_key_str = dep_key.get(); let dep_key_str = dep_key.get();
let dependency = crate::util::toml_mut::dependency::Dependency::from_toml( let dependency = crate::util::toml_mut::dependency::Dependency::from_toml(
ws.gctx(),
ws.root(),
&manifest_path, &manifest_path,
unstable_features,
dep_key_str, dep_key_str,
dep_item, dep_item,
)?; )?;
@ -472,7 +480,14 @@ pub fn write_manifest_upgrades(
dep.source = Some(Source::Registry(source)); dep.source = Some(Source::Registry(source));
trace!("upgrading dependency {name}"); trace!("upgrading dependency {name}");
dep.update_toml(&crate_root, &mut dep_key, dep_item); dep.update_toml(
ws.gctx(),
ws.root(),
&crate_root,
unstable_features,
&mut dep_key,
dep_item,
)?;
manifest_has_changed = true; manifest_has_changed = true;
any_file_has_changed = true; any_file_has_changed = true;
} }

View File

@ -1,16 +1,19 @@
//! Information about dependencies in a manifest. //! Information about dependencies in a manifest.
use std::borrow::Cow;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use cargo_util_schemas::manifest::PathBaseName;
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::Itertools; use itertools::Itertools;
use toml_edit::KeyMut; use toml_edit::KeyMut;
use super::manifest::str_or_1_len_table; use super::manifest::str_or_1_len_table;
use crate::core::GitReference;
use crate::core::SourceId; use crate::core::SourceId;
use crate::core::Summary; use crate::core::Summary;
use crate::core::{Features, GitReference};
use crate::util::toml::lookup_path_base;
use crate::util::toml_mut::is_sorted; use crate::util::toml_mut::is_sorted;
use crate::CargoResult; use crate::CargoResult;
use crate::GlobalContext; use crate::GlobalContext;
@ -219,7 +222,14 @@ pub enum MaybeWorkspace<T> {
impl Dependency { impl Dependency {
/// Create a dependency from a TOML table entry. /// Create a dependency from a TOML table entry.
pub fn from_toml(crate_root: &Path, key: &str, item: &toml_edit::Item) -> CargoResult<Self> { pub fn from_toml(
gctx: &GlobalContext,
workspace_root: &Path,
crate_root: &Path,
unstable_features: &Features,
key: &str,
item: &toml_edit::Item,
) -> CargoResult<Self> {
if let Some(version) = item.as_str() { if let Some(version) = item.as_str() {
let dep = Self::new(key).set_source(RegistrySource::new(version)); let dep = Self::new(key).set_source(RegistrySource::new(version));
Ok(dep) Ok(dep)
@ -266,12 +276,31 @@ impl Dependency {
} }
src.into() src.into()
} else if let Some(path) = table.get("path") { } else if let Some(path) = table.get("path") {
let base = table
.get("base")
.map(|base| {
base.as_str()
.ok_or_else(|| invalid_type(key, "base", base.type_name(), "string"))
.map(|s| s.to_owned())
})
.transpose()?;
let relative_to = if let Some(base) = &base {
Cow::Owned(lookup_path_base(
&PathBaseName::new(base.clone())?,
gctx,
&|| Ok(workspace_root),
unstable_features,
)?)
} else {
Cow::Borrowed(crate_root)
};
let path = let path =
crate_root relative_to
.join(path.as_str().ok_or_else(|| { .join(path.as_str().ok_or_else(|| {
invalid_type(key, "path", path.type_name(), "string") invalid_type(key, "path", path.type_name(), "string")
})?); })?);
let mut src = PathSource::new(path); let mut src = PathSource::new(path);
src.base = base;
if let Some(value) = table.get("version") { if let Some(value) = table.get("version") {
src = src.set_version(value.as_str().ok_or_else(|| { src = src.set_version(value.as_str().ok_or_else(|| {
invalid_type(key, "version", value.type_name(), "string") invalid_type(key, "version", value.type_name(), "string")
@ -372,7 +401,13 @@ impl Dependency {
/// # Panic /// # Panic
/// ///
/// Panics if the path is relative /// Panics if the path is relative
pub fn to_toml(&self, crate_root: &Path) -> toml_edit::Item { pub fn to_toml<'a>(
&self,
gctx: &GlobalContext,
workspace_root: &Path,
crate_root: &Path,
unstable_features: &Features,
) -> CargoResult<toml_edit::Item> {
assert!( assert!(
crate_root.is_absolute(), crate_root.is_absolute(),
"Absolute path needed, got: {}", "Absolute path needed, got: {}",
@ -412,10 +447,14 @@ impl Dependency {
table.insert("version", src.version.as_str().into()); table.insert("version", src.version.as_str().into());
} }
Some(Source::Path(src)) => { Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path); let relpath =
path_field(&src, gctx, workspace_root, crate_root, unstable_features)?;
if let Some(r) = src.version.as_deref() { if let Some(r) = src.version.as_deref() {
table.insert("version", r.into()); table.insert("version", r.into());
} }
if let Some(base) = &src.base {
table.insert("base", base.into());
}
table.insert("path", relpath.into()); table.insert("path", relpath.into());
} }
Some(Source::Git(src)) => { Some(Source::Git(src)) => {
@ -465,19 +504,22 @@ impl Dependency {
} }
}; };
table Ok(table)
} }
/// Modify existing entry to match this dependency. /// Modify existing entry to match this dependency.
pub fn update_toml<'k>( pub fn update_toml<'k, 'a>(
&self, &self,
gctx: &GlobalContext,
workspace_root: &Path,
crate_root: &Path, crate_root: &Path,
unstable_features: &Features,
key: &mut KeyMut<'k>, key: &mut KeyMut<'k>,
item: &mut toml_edit::Item, item: &mut toml_edit::Item,
) { ) -> CargoResult<()> {
if str_or_1_len_table(item) { if str_or_1_len_table(item) {
// Little to preserve // Little to preserve
let mut new_item = self.to_toml(crate_root); let mut new_item = self.to_toml(gctx, workspace_root, crate_root, unstable_features)?;
match (&item, &mut new_item) { match (&item, &mut new_item) {
(toml_edit::Item::Value(old), toml_edit::Item::Value(new)) => { (toml_edit::Item::Value(old), toml_edit::Item::Value(new)) => {
*new.decor_mut() = old.decor().clone(); *new.decor_mut() = old.decor().clone();
@ -493,12 +535,18 @@ impl Dependency {
Some(Source::Registry(src)) => { Some(Source::Registry(src)) => {
overwrite_value(table, "version", src.version.as_str()); 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); table.remove(key);
} }
} }
Some(Source::Path(src)) => { Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path); if let Some(base) = &src.base {
overwrite_value(table, "base", base);
} else {
table.remove("base");
}
let relpath =
path_field(&src, gctx, workspace_root, crate_root, unstable_features)?;
overwrite_value(table, "path", relpath); overwrite_value(table, "path", relpath);
if let Some(r) = src.version.as_deref() { if let Some(r) = src.version.as_deref() {
overwrite_value(table, "version", r); overwrite_value(table, "version", r);
@ -533,7 +581,7 @@ impl Dependency {
table.remove("version"); table.remove("version");
} }
for key in ["path", "workspace"] { for key in ["path", "workspace", "base"] {
table.remove(key); table.remove(key);
} }
} }
@ -552,6 +600,7 @@ impl Dependency {
"rev", "rev",
"package", "package",
"default-features", "default-features",
"base",
] { ] {
table.remove(key); table.remove(key);
} }
@ -623,6 +672,7 @@ impl Dependency {
} else { } else {
unreachable!("Invalid dependency type: {}", item.type_name()); unreachable!("Invalid dependency type: {}", item.type_name());
} }
Ok(())
} }
} }
@ -682,10 +732,26 @@ impl From<Summary> for Dependency {
} }
} }
fn path_field(crate_root: &Path, abs_path: &Path) -> String { fn path_field<'a>(
let relpath = pathdiff::diff_paths(abs_path, crate_root).expect("both paths are absolute"); source: &PathSource,
gctx: &GlobalContext,
workspace_root: &Path,
crate_root: &Path,
unstable_features: &Features,
) -> CargoResult<String> {
let relative_to = if let Some(base) = &source.base {
Cow::Owned(lookup_path_base(
&PathBaseName::new(base.clone())?,
gctx,
&|| Ok(workspace_root),
unstable_features,
)?)
} else {
Cow::Borrowed(crate_root)
};
let relpath = pathdiff::diff_paths(&source.path, relative_to).expect("both paths are absolute");
let relpath = relpath.to_str().unwrap().replace('\\', "/"); let relpath = relpath.to_str().unwrap().replace('\\', "/");
relpath Ok(relpath)
} }
/// Primary location of a dependency. /// Primary location of a dependency.
@ -812,6 +878,8 @@ impl std::fmt::Display for RegistrySource {
pub struct PathSource { pub struct PathSource {
/// Local, absolute path. /// Local, absolute path.
pub path: PathBuf, pub path: PathBuf,
/// The path base, if using one.
pub base: Option<String>,
/// Version requirement for when published. /// Version requirement for when published.
pub version: Option<String>, pub version: Option<String>,
} }
@ -821,6 +889,7 @@ impl PathSource {
pub fn new(path: impl Into<PathBuf>) -> Self { pub fn new(path: impl Into<PathBuf>) -> Self {
Self { Self {
path: path.into(), path: path.into(),
base: None,
version: None, version: None,
} }
} }
@ -975,11 +1044,14 @@ mod tests {
paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/"))); paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/")));
let dep = Dependency::new("dep").set_source(RegistrySource::new("1.0")); let dep = Dependency::new("dep").set_source(RegistrySource::new("1.0"));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -988,12 +1060,15 @@ mod tests {
paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/"))); paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/")));
let dep = Dependency::new("dep").set_source(RegistrySource::new("1.0")); let dep = Dependency::new("dep").set_source(RegistrySource::new("1.0"));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert_eq!(item.as_str(), Some("1.0")); assert_eq!(item.as_str(), Some("1.0"));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1004,7 +1079,10 @@ mod tests {
.set_source(RegistrySource::new("1.0")) .set_source(RegistrySource::new("1.0"))
.set_optional(true); .set_optional(true);
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1012,7 +1090,7 @@ mod tests {
let dep = item.as_inline_table().unwrap(); let dep = item.as_inline_table().unwrap();
assert_eq!(dep.get("optional").unwrap().as_bool(), Some(true)); assert_eq!(dep.get("optional").unwrap().as_bool(), Some(true));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1023,7 +1101,10 @@ mod tests {
.set_source(RegistrySource::new("1.0")) .set_source(RegistrySource::new("1.0"))
.set_default_features(false); .set_default_features(false);
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1031,7 +1112,7 @@ mod tests {
let dep = item.as_inline_table().unwrap(); let dep = item.as_inline_table().unwrap();
assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false)); assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1040,7 +1121,10 @@ mod tests {
let crate_root = root.join("foo"); let crate_root = root.join("foo");
let dep = Dependency::new("dep").set_source(PathSource::new(root.join("bar"))); let dep = Dependency::new("dep").set_source(PathSource::new(root.join("bar")));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1048,7 +1132,7 @@ mod tests {
let dep = item.as_inline_table().unwrap(); let dep = item.as_inline_table().unwrap();
assert_eq!(dep.get("path").unwrap().as_str(), Some("../bar")); assert_eq!(dep.get("path").unwrap().as_str(), Some("../bar"));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1057,7 +1141,10 @@ mod tests {
paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/"))); paths::normalize_path(&std::env::current_dir().unwrap().join(Path::new("/")));
let dep = Dependency::new("dep").set_source(GitSource::new("https://foor/bar.git")); let dep = Dependency::new("dep").set_source(GitSource::new("https://foor/bar.git"));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1068,7 +1155,7 @@ mod tests {
Some("https://foor/bar.git") Some("https://foor/bar.git")
); );
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1079,7 +1166,10 @@ mod tests {
.set_source(RegistrySource::new("1.0")) .set_source(RegistrySource::new("1.0"))
.set_rename("d"); .set_rename("d");
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "d".to_owned()); assert_eq!(key, "d".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1087,7 +1177,7 @@ mod tests {
let dep = item.as_inline_table().unwrap(); let dep = item.as_inline_table().unwrap();
assert_eq!(dep.get("package").unwrap().as_str(), Some("dep")); assert_eq!(dep.get("package").unwrap().as_str(), Some("dep"));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1098,7 +1188,10 @@ mod tests {
.set_source(RegistrySource::new("1.0")) .set_source(RegistrySource::new("1.0"))
.set_registry("alternative"); .set_registry("alternative");
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "dep".to_owned()); assert_eq!(key, "dep".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1106,7 +1199,7 @@ mod tests {
let dep = item.as_inline_table().unwrap(); let dep = item.as_inline_table().unwrap();
assert_eq!(dep.get("registry").unwrap().as_str(), Some("alternative")); assert_eq!(dep.get("registry").unwrap().as_str(), Some("alternative"));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1118,7 +1211,10 @@ mod tests {
.set_default_features(false) .set_default_features(false)
.set_rename("d"); .set_rename("d");
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
assert_eq!(key, "d".to_owned()); assert_eq!(key, "d".to_owned());
assert!(item.is_inline_table()); assert!(item.is_inline_table());
@ -1128,7 +1224,7 @@ mod tests {
assert_eq!(dep.get("version").unwrap().as_str(), Some("1.0")); assert_eq!(dep.get("version").unwrap().as_str(), Some("1.0"));
assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false)); assert_eq!(dep.get("default-features").unwrap().as_bool(), Some(false));
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1139,13 +1235,16 @@ mod tests {
let relpath = "sibling/crate"; let relpath = "sibling/crate";
let dep = Dependency::new("dep").set_source(PathSource::new(path)); let dep = Dependency::new("dep").set_source(PathSource::new(path));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
let table = item.as_inline_table().unwrap(); let table = item.as_inline_table().unwrap();
let got = table.get("path").unwrap().as_str().unwrap(); let got = table.get("path").unwrap().as_str().unwrap();
assert_eq!(got, relpath); assert_eq!(got, relpath);
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[test] #[test]
@ -1159,10 +1258,21 @@ mod tests {
manifest, manifest,
}; };
assert_eq!(local.manifest.to_string(), toml); assert_eq!(local.manifest.to_string(), toml);
let gctx = GlobalContext::default().unwrap();
for (key, item) in local.data.clone().iter() { for (key, item) in local.data.clone().iter() {
let dep = Dependency::from_toml(&crate_root, key, item).unwrap(); let dep = Dependency::from_toml(
&gctx,
&crate_root,
&crate_root,
&Features::default(),
key,
item,
)
.unwrap();
let dep = dep.set_source(WorkspaceSource::new()); let dep = dep.set_source(WorkspaceSource::new());
local.insert_into_table(&vec![], &dep).unwrap(); local
.insert_into_table(&vec![], &dep, &gctx, &crate_root, &Features::default())
.unwrap();
assert_eq!(local.data.to_string(), "dep.workspace = true\n"); assert_eq!(local.data.to_string(), "dep.workspace = true\n");
} }
} }
@ -1176,20 +1286,38 @@ mod tests {
let should_be = "sibling/crate"; let should_be = "sibling/crate";
let dep = Dependency::new("dep").set_source(PathSource::new(original)); let dep = Dependency::new("dep").set_source(PathSource::new(original));
let key = dep.toml_key(); let key = dep.toml_key();
let item = dep.to_toml(&crate_root); let gctx = GlobalContext::default().unwrap();
let item = dep
.to_toml(&gctx, &crate_root, &crate_root, &Features::default())
.unwrap();
let table = item.as_inline_table().unwrap(); let table = item.as_inline_table().unwrap();
let got = table.get("path").unwrap().as_str().unwrap(); let got = table.get("path").unwrap().as_str().unwrap();
assert_eq!(got, should_be); assert_eq!(got, should_be);
verify_roundtrip(&crate_root, key, &item); verify_roundtrip(&crate_root, &gctx, key, &item);
} }
#[track_caller] #[track_caller]
fn verify_roundtrip(crate_root: &Path, key: &str, item: &toml_edit::Item) { fn verify_roundtrip(
let roundtrip = Dependency::from_toml(crate_root, key, item).unwrap(); crate_root: &Path,
gctx: &GlobalContext,
key: &str,
item: &toml_edit::Item,
) {
let roundtrip = Dependency::from_toml(
gctx,
crate_root,
crate_root,
&Features::default(),
key,
item,
)
.unwrap();
let round_key = roundtrip.toml_key(); let round_key = roundtrip.toml_key();
let round_item = roundtrip.to_toml(crate_root); let round_item = roundtrip
.to_toml(gctx, crate_root, crate_root, &Features::default())
.unwrap();
assert_eq!(key, round_key); assert_eq!(key, round_key);
assert_eq!(item.to_string(), round_item.to_string()); assert_eq!(item.to_string(), round_item.to_string());
} }

View File

@ -8,9 +8,9 @@ use anyhow::Context as _;
use super::dependency::Dependency; use super::dependency::Dependency;
use crate::core::dependency::DepKind; use crate::core::dependency::DepKind;
use crate::core::FeatureValue; use crate::core::{FeatureValue, Features, Workspace};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::CargoResult; use crate::{CargoResult, GlobalContext};
/// Dependency table to add deps to. /// Dependency table to add deps to.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -286,6 +286,8 @@ impl LocalManifest {
pub fn get_dependency_versions<'s>( pub fn get_dependency_versions<'s>(
&'s self, &'s self,
dep_key: &'s str, dep_key: &'s str,
ws: &'s Workspace<'_>,
unstable_features: &'s Features,
) -> impl Iterator<Item = (DepTable, CargoResult<Dependency>)> + 's { ) -> impl Iterator<Item = (DepTable, CargoResult<Dependency>)> + 's {
let crate_root = self.path.parent().expect("manifest path is absolute"); let crate_root = self.path.parent().expect("manifest path is absolute");
self.get_sections() self.get_sections()
@ -307,7 +309,14 @@ impl LocalManifest {
}) })
.flatten() .flatten()
.map(move |(table_path, dep_key, dep_item)| { .map(move |(table_path, dep_key, dep_item)| {
let dep = Dependency::from_toml(crate_root, &dep_key, &dep_item); let dep = Dependency::from_toml(
ws.gctx(),
ws.root(),
crate_root,
unstable_features,
&dep_key,
&dep_item,
);
(table_path, dep) (table_path, dep)
}) })
} }
@ -317,6 +326,9 @@ impl LocalManifest {
&mut self, &mut self,
table_path: &[String], table_path: &[String],
dep: &Dependency, dep: &Dependency,
gctx: &GlobalContext,
workspace_root: &Path,
unstable_features: &Features,
) -> CargoResult<()> { ) -> CargoResult<()> {
let crate_root = self let crate_root = self
.path .path
@ -331,7 +343,14 @@ impl LocalManifest {
.unwrap() .unwrap()
.get_key_value_mut(dep_key) .get_key_value_mut(dep_key)
{ {
dep.update_toml(&crate_root, &mut dep_key, dep_item); dep.update_toml(
gctx,
workspace_root,
&crate_root,
unstable_features,
&mut dep_key,
dep_item,
)?;
if let Some(table) = dep_item.as_inline_table_mut() { if let Some(table) = dep_item.as_inline_table_mut() {
// So long as we don't have `Cargo.toml` auto-formatting and inline-tables can only // So long as we don't have `Cargo.toml` auto-formatting and inline-tables can only
// be on one line, there isn't really much in the way of interesting formatting to // be on one line, there isn't really much in the way of interesting formatting to
@ -339,7 +358,8 @@ impl LocalManifest {
table.fmt(); table.fmt();
} }
} else { } else {
let new_dependency = dep.to_toml(&crate_root); let new_dependency =
dep.to_toml(gctx, workspace_root, &crate_root, unstable_features)?;
table[dep_key] = new_dependency; table[dep_key] = new_dependency;
} }

View File

@ -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. [Filesystem path](../reference/specifying-dependencies.html#specifying-path-dependencies) to local crate to add.
{{/option}} {{/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-registry }}
{{/options}} {{/options}}

View File

@ -56,6 +56,14 @@ OPTIONS
<https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies> <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies>
to local crate to add. 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 --registry registry
Name of the registry to use. Registry names are defined in Cargo Name of the registry to use. Registry names are defined in Cargo
config files config files

View File

@ -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> <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> <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 <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, files</a>. If not specified, the default registry is used,

View File

@ -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. \fIFilesystem path\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.html#specifying\-path\-dependencies> to local crate to add.
.RE .RE
.sp .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 \fB\-\-registry\fR \fIregistry\fR
.RS 4 .RS 4
Name of the registry to use. Registry names are defined in \fICargo config Name of the registry to use. Registry names are defined in \fICargo config

View File

@ -0,0 +1,2 @@
[path-bases]
my_base = "deps"

View File

@ -0,0 +1,7 @@
cargo-features = ["path-bases"]
[workspace]
members = ["primary", "deps/dependency"]
[workspace.dependencies]
foo = { version = "0.0.0", base = "my_base", path = "./dependency"}

View File

@ -0,0 +1,4 @@
[package]
name = "foo"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,6 @@
cargo-features = ["path-bases"]
[package]
name = "bar"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,27 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = &project_root;
snapbox::cmd::Command::cargo_ui()
.arg("add")
.args(["foo", "-p", "bar"])
.current_dir(cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.success()
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,7 @@
cargo-features = ["path-bases"]
[workspace]
members = ["primary", "deps/dependency"]
[workspace.dependencies]
foo = { version = "0.0.0", base = "my_base", path = "./dependency"}

View File

@ -0,0 +1,4 @@
[package]
name = "foo"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,9 @@
cargo-features = ["path-bases"]
[package]
name = "bar"
version = "0.0.0"
edition = "2015"
[dependencies]
foo.workspace = true

View File

@ -0,0 +1,27 @@
<svg width="740px" height="56px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-green { fill: #00AA00 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-green bold"> Adding</tspan><tspan> foo (workspace) to dependencies</tspan>
</tspan>
<tspan x="10px" y="46px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@ -0,0 +1,2 @@
[path-bases]
my_base = "."

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,11 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { version = "0.0.0", base = "my_base", path = "dependency" }

View File

@ -0,0 +1,25 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("cargo-list-test-fixture-dependency --dev")
.current_dir(&cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.success()
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,14 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { version = "0.0.0", base = "my_base", path = "dependency" }
[dev-dependencies]
cargo-list-test-fixture-dependency = { base = "my_base", path = "dependency" }

View File

@ -0,0 +1,29 @@
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-green { fill: #00AA00 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-green bold"> Adding</tspan><tspan> cargo-list-test-fixture-dependency (local) to dev-dependencies</tspan>
</tspan>
<tspan x="10px" y="46px"><tspan class="fg-green bold"> Locking</tspan><tspan> 1 package to latest compatible version</tspan>
</tspan>
<tspan x="10px" y="64px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 911 B

View File

@ -219,83 +219,89 @@
</tspan> </tspan>
<tspan x="10px" y="1810px"> <tspan x="10px" y="1810px">
</tspan> </tspan>
<tspan x="10px" y="1828px"><tspan> </tspan><tspan class="fg-cyan bold">--git</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;URI&gt;</tspan> <tspan x="10px" y="1828px"><tspan> </tspan><tspan class="fg-cyan bold">--base</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;BASE&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="1846px"><tspan> Git repository location</tspan> <tspan x="10px" y="1846px"><tspan> The path base to use when adding from a local crate (unstable).</tspan>
</tspan> </tspan>
<tspan x="10px" y="1864px"><tspan> </tspan> <tspan x="10px" y="1864px">
</tspan> </tspan>
<tspan x="10px" y="1882px"><tspan> Without any other information, cargo will use latest commit on the main branch.</tspan> <tspan x="10px" y="1882px"><tspan> </tspan><tspan class="fg-cyan bold">--git</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;URI&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="1900px"> <tspan x="10px" y="1900px"><tspan> Git repository location</tspan>
</tspan> </tspan>
<tspan x="10px" y="1918px"><tspan> </tspan><tspan class="fg-cyan bold">--branch</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;BRANCH&gt;</tspan> <tspan x="10px" y="1918px"><tspan> </tspan>
</tspan> </tspan>
<tspan x="10px" y="1936px"><tspan> Git branch to download the crate from</tspan> <tspan x="10px" y="1936px"><tspan> Without any other information, cargo will use latest commit on the main branch.</tspan>
</tspan> </tspan>
<tspan x="10px" y="1954px"> <tspan x="10px" y="1954px">
</tspan> </tspan>
<tspan x="10px" y="1972px"><tspan> </tspan><tspan class="fg-cyan bold">--tag</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;TAG&gt;</tspan> <tspan x="10px" y="1972px"><tspan> </tspan><tspan class="fg-cyan bold">--branch</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;BRANCH&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="1990px"><tspan> Git tag to download the crate from</tspan> <tspan x="10px" y="1990px"><tspan> Git branch to download the crate from</tspan>
</tspan> </tspan>
<tspan x="10px" y="2008px"> <tspan x="10px" y="2008px">
</tspan> </tspan>
<tspan x="10px" y="2026px"><tspan> </tspan><tspan class="fg-cyan bold">--rev</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;REV&gt;</tspan> <tspan x="10px" y="2026px"><tspan> </tspan><tspan class="fg-cyan bold">--tag</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;TAG&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="2044px"><tspan> Git reference to download the crate from</tspan> <tspan x="10px" y="2044px"><tspan> Git tag to download the crate from</tspan>
</tspan> </tspan>
<tspan x="10px" y="2062px"><tspan> </tspan> <tspan x="10px" y="2062px">
</tspan> </tspan>
<tspan x="10px" y="2080px"><tspan> This is the catch all, handling hashes to named references in remote repositories.</tspan> <tspan x="10px" y="2080px"><tspan> </tspan><tspan class="fg-cyan bold">--rev</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;REV&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="2098px"> <tspan x="10px" y="2098px"><tspan> Git reference to download the crate from</tspan>
</tspan> </tspan>
<tspan x="10px" y="2116px"><tspan> </tspan><tspan class="fg-cyan bold">--registry</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;NAME&gt;</tspan> <tspan x="10px" y="2116px"><tspan> </tspan>
</tspan> </tspan>
<tspan x="10px" y="2134px"><tspan> Package registry for this dependency</tspan> <tspan x="10px" y="2134px"><tspan> This is the catch all, handling hashes to named references in remote repositories.</tspan>
</tspan> </tspan>
<tspan x="10px" y="2152px"> <tspan x="10px" y="2152px">
</tspan> </tspan>
<tspan x="10px" y="2170px"><tspan class="fg-green bold">Section:</tspan> <tspan x="10px" y="2170px"><tspan> </tspan><tspan class="fg-cyan bold">--registry</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;NAME&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="2188px"><tspan> </tspan><tspan class="fg-cyan bold">--dev</tspan> <tspan x="10px" y="2188px"><tspan> Package registry for this dependency</tspan>
</tspan> </tspan>
<tspan x="10px" y="2206px"><tspan> Add as development dependency</tspan> <tspan x="10px" y="2206px">
</tspan> </tspan>
<tspan x="10px" y="2224px"><tspan> </tspan> <tspan x="10px" y="2224px"><tspan class="fg-green bold">Section:</tspan>
</tspan> </tspan>
<tspan x="10px" y="2242px"><tspan> Dev-dependencies are not used when compiling a package for building, but are used for</tspan> <tspan x="10px" y="2242px"><tspan> </tspan><tspan class="fg-cyan bold">--dev</tspan>
</tspan> </tspan>
<tspan x="10px" y="2260px"><tspan> compiling tests, examples, and benchmarks.</tspan> <tspan x="10px" y="2260px"><tspan> Add as development dependency</tspan>
</tspan> </tspan>
<tspan x="10px" y="2278px"><tspan> </tspan> <tspan x="10px" y="2278px"><tspan> </tspan>
</tspan> </tspan>
<tspan x="10px" y="2296px"><tspan> These dependencies are not propagated to other packages which depend on this package.</tspan> <tspan x="10px" y="2296px"><tspan> Dev-dependencies are not used when compiling a package for building, but are used for</tspan>
</tspan> </tspan>
<tspan x="10px" y="2314px"> <tspan x="10px" y="2314px"><tspan> compiling tests, examples, and benchmarks.</tspan>
</tspan> </tspan>
<tspan x="10px" y="2332px"><tspan> </tspan><tspan class="fg-cyan bold">--build</tspan> <tspan x="10px" y="2332px"><tspan> </tspan>
</tspan> </tspan>
<tspan x="10px" y="2350px"><tspan> Add as build dependency</tspan> <tspan x="10px" y="2350px"><tspan> These dependencies are not propagated to other packages which depend on this package.</tspan>
</tspan> </tspan>
<tspan x="10px" y="2368px"><tspan> </tspan> <tspan x="10px" y="2368px">
</tspan> </tspan>
<tspan x="10px" y="2386px"><tspan> Build-dependencies are the only dependencies available for use by build scripts</tspan> <tspan x="10px" y="2386px"><tspan> </tspan><tspan class="fg-cyan bold">--build</tspan>
</tspan> </tspan>
<tspan x="10px" y="2404px"><tspan> (`build.rs` files).</tspan> <tspan x="10px" y="2404px"><tspan> Add as build dependency</tspan>
</tspan> </tspan>
<tspan x="10px" y="2422px"> <tspan x="10px" y="2422px"><tspan> </tspan>
</tspan> </tspan>
<tspan x="10px" y="2440px"><tspan> </tspan><tspan class="fg-cyan bold">--target</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;TARGET&gt;</tspan> <tspan x="10px" y="2440px"><tspan> Build-dependencies are the only dependencies available for use by build scripts</tspan>
</tspan> </tspan>
<tspan x="10px" y="2458px"><tspan> Add as dependency to the given target platform</tspan> <tspan x="10px" y="2458px"><tspan> (`build.rs` files).</tspan>
</tspan> </tspan>
<tspan x="10px" y="2476px"> <tspan x="10px" y="2476px">
</tspan> </tspan>
<tspan x="10px" y="2494px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help add</tspan><tspan class="bold">` for more detailed information.</tspan> <tspan x="10px" y="2494px"><tspan> </tspan><tspan class="fg-cyan bold">--target</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;TARGET&gt;</tspan>
</tspan> </tspan>
<tspan x="10px" y="2512px"> <tspan x="10px" y="2512px"><tspan> Add as dependency to the given target platform</tspan>
</tspan>
<tspan x="10px" y="2530px">
</tspan>
<tspan x="10px" y="2548px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help add</tspan><tspan class="bold">` for more detailed information.</tspan>
</tspan>
<tspan x="10px" y="2566px">
</tspan> </tspan>
</text> </text>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -16,9 +16,11 @@ mod detect_workspace_inherit;
mod detect_workspace_inherit_features; mod detect_workspace_inherit_features;
mod detect_workspace_inherit_fuzzy; mod detect_workspace_inherit_fuzzy;
mod detect_workspace_inherit_optional; mod detect_workspace_inherit_optional;
mod detect_workspace_inherit_path_base;
mod detect_workspace_inherit_public; mod detect_workspace_inherit_public;
mod dev; mod dev;
mod dev_build_conflict; mod dev_build_conflict;
mod dev_existing_path_base;
mod dev_prefer_existing_version; mod dev_prefer_existing_version;
mod dry_run; mod dry_run;
mod empty_dep_name; mod empty_dep_name;
@ -94,6 +96,7 @@ mod overwrite_no_public_with_public;
mod overwrite_optional; mod overwrite_optional;
mod overwrite_optional_with_no_optional; mod overwrite_optional_with_no_optional;
mod overwrite_optional_with_optional; mod overwrite_optional_with_optional;
mod overwrite_path_base_with_version;
mod overwrite_path_noop; mod overwrite_path_noop;
mod overwrite_path_with_version; mod overwrite_path_with_version;
mod overwrite_preserves_inline_table; mod overwrite_preserves_inline_table;
@ -108,6 +111,10 @@ mod overwrite_with_rename;
mod overwrite_workspace_dep; mod overwrite_workspace_dep;
mod overwrite_workspace_dep_features; mod overwrite_workspace_dep_features;
mod path; mod path;
mod path_base;
mod path_base_inferred_name;
mod path_base_missing_base_path;
mod path_base_unstable;
mod path_dev; mod path_dev;
mod path_inferred_name; mod path_inferred_name;
mod path_inferred_name_conflicts_full_feature; mod path_inferred_name_conflicts_full_feature;

View File

@ -0,0 +1,2 @@
[path-bases]
my_base = "."

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,11 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { optional = true, path = "dependency", base = "my_base" }

View File

@ -0,0 +1,29 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", "20.0.0")
.publish();
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("cargo-list-test-fixture-dependency@20.0")
.current_dir(&cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.success()
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,14 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { optional = true, version = "20.0" }
[features]
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

View File

@ -0,0 +1,35 @@
<svg width="844px" height="128px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-cyan { fill: #00AAAA }
.fg-green { fill: #00AA00 }
.fg-yellow { fill: #AA5500 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-green bold"> Updating</tspan><tspan> `dummy-registry` index</tspan>
</tspan>
<tspan x="10px" y="46px"><tspan class="fg-green bold"> Adding</tspan><tspan> cargo-list-test-fixture-dependency v20.0 to optional dependencies</tspan>
</tspan>
<tspan x="10px" y="64px"><tspan class="fg-green bold"> Adding</tspan><tspan> feature `cargo-list-test-fixture-dependency`</tspan>
</tspan>
<tspan x="10px" y="82px"><tspan class="fg-green bold"> Locking</tspan><tspan> 1 package to latest compatible version</tspan>
</tspan>
<tspan x="10px" y="100px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,2 @@
[path-bases]
my_base = "."

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,8 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,25 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("cargo-list-test-fixture-dependency --path ../dependency --base my_base")
.current_dir(&cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.success()
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,11 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { version = "0.0.0", base = "my_base", path = "dependency" }

View File

@ -0,0 +1,29 @@
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-green { fill: #00AA00 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-green bold"> Adding</tspan><tspan> cargo-list-test-fixture-dependency (local) to dependencies</tspan>
</tspan>
<tspan x="10px" y="46px"><tspan class="fg-green bold"> Locking</tspan><tspan> 1 package to latest compatible version</tspan>
</tspan>
<tspan x="10px" y="64px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 907 B

View File

@ -0,0 +1,2 @@
[path-bases]
my_base = "."

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,8 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,25 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("--path ../dependency --base my_base")
.current_dir(&cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.success()
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,6 @@
[workspace]
[package]
name = "cargo-list-test-fixture-dependency"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,11 @@
cargo-features = ["path-bases"]
[workspace]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"
[dependencies]
cargo-list-test-fixture-dependency = { version = "0.0.0", base = "my_base", path = "dependency" }

View File

@ -0,0 +1,29 @@
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-green { fill: #00AA00 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-green bold"> Adding</tspan><tspan> cargo-list-test-fixture-dependency (local) to dependencies</tspan>
</tspan>
<tspan x="10px" y="46px"><tspan class="fg-green bold"> Locking</tspan><tspan> 1 package to latest compatible version</tspan>
</tspan>
<tspan x="10px" y="64px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 907 B

View File

@ -0,0 +1,6 @@
cargo-features = ["path-bases"]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,25 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("--path dependency --base bad_base")
.current_dir(&cwd)
.masquerade_as_nightly_cargo(&["path-base"])
.assert()
.code(101)
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,6 @@
cargo-features = ["path-bases"]
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,27 @@
<svg width="740px" height="56px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-red { fill: #AA0000 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-red bold">error</tspan><tspan class="bold">:</tspan><tspan> path base `bad_base` is undefined. You must add an entry for `bad_base` in the Cargo configuration [path-bases] table.</tspan>
</tspan>
<tspan x="10px" y="46px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 843 B

View File

@ -0,0 +1,4 @@
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,24 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::current_dir;
use cargo_test_support::file;
use cargo_test_support::prelude::*;
use cargo_test_support::str;
use cargo_test_support::Project;
#[cargo_test]
fn case() {
let project = Project::from_template(current_dir!().join("in"));
let project_root = project.root();
let cwd = project_root.join("primary");
snapbox::cmd::Command::cargo_ui()
.arg("add")
.arg_line("--path dependency --base mybase")
.current_dir(&cwd)
.assert()
.code(101)
.stdout_eq(str![""])
.stderr_eq(file!["stderr.term.svg"]);
assert_ui().subset_matches(current_dir!().join("out"), &project_root);
}

View File

@ -0,0 +1,4 @@
[package]
name = "cargo-list-test-fixture"
version = "0.0.0"
edition = "2015"

View File

@ -0,0 +1,37 @@
<svg width="740px" height="56px" xmlns="http://www.w3.org/2000/svg">
<style>
.fg { fill: #AAAAAA }
.bg { background: #000000 }
.fg-red { fill: #AA0000 }
.container {
padding: 0 10px;
line-height: 18px;
}
.bold { font-weight: bold; }
tspan {
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: pre;
line-height: 18px;
}
</style>
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
<text xml:space="preserve" class="container fg">
<tspan x="10px" y="28px"><tspan class="fg-red bold">error</tspan><tspan class="bold">:</tspan><tspan> feature `path-bases` is required</tspan>
</tspan>
<tspan x="10px" y="46px">
</tspan>
<tspan x="10px" y="64px"><tspan>The package requires the Cargo feature called `path-bases`, but that feature is not stabilized in this version of Cargo ([..]).</tspan>
</tspan>
<tspan x="10px" y="82px"><tspan>Consider trying a newer version of Cargo (this may require the nightly release).</tspan>
</tspan>
<tspan x="10px" y="100px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#path-bases for more information about the status of this feature.</tspan>
</tspan>
<tspan x="10px" y="118px">
</tspan>
<tspan x="10px" y="136px">
</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB