Skip to content

Commit eaf3848

Browse files
authored
Merge pull request #732 from epage/resolve
fix: Allow relative manifest paths
2 parents 07c27ad + 5e3fb4f commit eaf3848

25 files changed

+178
-182
lines changed

src/bin/set-version/set_version.rs

+6-54
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::path::Path;
33
use std::path::PathBuf;
44

55
use cargo_edit::{
6-
colorize_stderr, find, manifest_from_pkgid, upgrade_requirement, workspace_members,
7-
LocalManifest,
6+
colorize_stderr, resolve_manifests, upgrade_requirement, workspace_members, LocalManifest,
87
};
98
use clap::Args;
109
use termcolor::{BufferWriter, Color, ColorSpec, WriteColor};
@@ -110,13 +109,11 @@ fn exec(args: VersionArgs) -> CargoResult<()> {
110109
deprecated_message("The flag `--all` has been deprecated in favor of `--workspace`")?;
111110
}
112111
let all = workspace || all;
113-
let manifests = if all {
114-
Manifests::get_all(manifest_path.as_deref())
115-
} else if let Some(ref pkgid) = pkgid {
116-
Manifests::get_pkgid(manifest_path.as_deref(), pkgid)
117-
} else {
118-
Manifests::get_local_one(manifest_path.as_deref())
119-
}?;
112+
let manifests = Manifests(resolve_manifests(
113+
manifest_path.as_deref(),
114+
all,
115+
pkgid.as_deref().into_iter().collect::<Vec<_>>(),
116+
)?);
120117

121118
if dry_run {
122119
dry_run_message()?;
@@ -190,51 +187,6 @@ fn exec(args: VersionArgs) -> CargoResult<()> {
190187
/// A collection of manifests.
191188
struct Manifests(Vec<cargo_metadata::Package>);
192189

193-
impl Manifests {
194-
/// Get all manifests in the workspace.
195-
fn get_all(manifest_path: Option<&Path>) -> CargoResult<Self> {
196-
let mut cmd = cargo_metadata::MetadataCommand::new();
197-
cmd.no_deps();
198-
if let Some(path) = manifest_path {
199-
cmd.manifest_path(path);
200-
}
201-
let result = cmd
202-
.exec()
203-
.with_context(|| "Failed to get workspace metadata")?;
204-
Ok(Self(result.packages))
205-
}
206-
207-
fn get_pkgid(manifest_path: Option<&Path>, pkgid: &str) -> CargoResult<Self> {
208-
let package = manifest_from_pkgid(manifest_path, pkgid)?;
209-
Ok(Manifests(vec![package]))
210-
}
211-
212-
/// Get the manifest specified by the manifest path. Try to make an educated guess if no path is
213-
/// provided.
214-
fn get_local_one(manifest_path: Option<&Path>) -> CargoResult<Self> {
215-
let resolved_manifest_path: String = find(manifest_path)?.to_string_lossy().into();
216-
217-
let mut cmd = cargo_metadata::MetadataCommand::new();
218-
cmd.no_deps();
219-
if let Some(path) = manifest_path {
220-
cmd.manifest_path(path);
221-
}
222-
let result = cmd.exec().with_context(|| "Invalid manifest")?;
223-
let packages = result.packages;
224-
let package = packages
225-
.iter()
226-
.find(|p| p.manifest_path == resolved_manifest_path)
227-
// If we have successfully got metadata, but our manifest path does not correspond to a
228-
// package, we must have been called against a virtual manifest.
229-
.with_context(|| {
230-
"Found virtual manifest, but this command requires running against an \
231-
actual package in this workspace. Try adding `--workspace`."
232-
})?;
233-
234-
Ok(Manifests(vec![package.to_owned()]))
235-
}
236-
}
237-
238190
fn dry_run_message() -> CargoResult<()> {
239191
let colorchoice = colorize_stderr();
240192
let bufwtr = BufferWriter::stderr(colorchoice);

src/bin/upgrade/upgrade.rs

+16-81
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::collections::BTreeSet;
22
use std::io::Write;
3-
use std::path::{Path, PathBuf};
3+
use std::path::PathBuf;
44

55
use cargo_edit::{
6-
colorize_stderr, find, get_latest_dependency, manifest_from_pkgid, registry_url,
7-
set_dep_version, shell_status, shell_warn, update_registry_index, CargoResult, Context,
8-
CrateSpec, Dependency, LocalManifest,
6+
colorize_stderr, find, get_latest_dependency, registry_url, resolve_manifests, set_dep_version,
7+
shell_status, shell_warn, update_registry_index, CargoResult, Context, CrateSpec, Dependency,
8+
LocalManifest,
99
};
1010
use clap::Args;
1111
use indexmap::IndexMap;
@@ -48,7 +48,7 @@ pub struct UpgradeArgs {
4848
conflicts_with = "all",
4949
conflicts_with = "workspace"
5050
)]
51-
pkgid: Option<String>,
51+
pkgid: Vec<String>,
5252

5353
/// Upgrade all packages in the workspace.
5454
#[clap(
@@ -114,14 +114,12 @@ impl UpgradeArgs {
114114
self.all || self.workspace
115115
}
116116

117-
fn resolve_targets(&self) -> CargoResult<Vec<(LocalManifest, cargo_metadata::Package)>> {
118-
if self.workspace() {
119-
resolve_all(self.manifest_path.as_deref())
120-
} else if let Some(pkgid) = self.pkgid.as_deref() {
121-
resolve_pkgid(self.manifest_path.as_deref(), pkgid)
122-
} else {
123-
resolve_local_one(self.manifest_path.as_deref())
124-
}
117+
fn resolve_targets(&self) -> CargoResult<Vec<cargo_metadata::Package>> {
118+
resolve_manifests(
119+
self.manifest_path.as_deref(),
120+
self.workspace(),
121+
self.pkgid.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
122+
)
125123
}
126124

127125
fn verbose<F>(&self, mut callback: F) -> CargoResult<()>
@@ -170,7 +168,8 @@ fn exec(args: UpgradeArgs) -> CargoResult<()> {
170168

171169
let mut updated_registries = BTreeSet::new();
172170
let mut any_crate_modified = false;
173-
for (mut manifest, package) in manifests {
171+
for package in manifests {
172+
let mut manifest = LocalManifest::try_new(package.manifest_path.as_std_path())?;
174173
let mut crate_modified = false;
175174
let manifest_path = manifest.path.clone();
176175
shell_status("Checking", &format!("{}'s dependencies", package.name))?;
@@ -363,17 +362,15 @@ fn exec(args: UpgradeArgs) -> CargoResult<()> {
363362
Ok(())
364363
}
365364

366-
fn load_lockfile(
367-
targets: &[(LocalManifest, cargo_metadata::Package)],
368-
) -> CargoResult<Vec<cargo_metadata::Package>> {
365+
fn load_lockfile(targets: &[cargo_metadata::Package]) -> CargoResult<Vec<cargo_metadata::Package>> {
369366
// Get locked dependencies. For workspaces with multiple Cargo.toml
370367
// files, there is only a single lockfile, so it suffices to get
371368
// metadata for any one of Cargo.toml files.
372-
let (manifest, _package) = targets
369+
let package = targets
373370
.get(0)
374371
.ok_or_else(|| anyhow::format_err!("Invalid cargo config"))?;
375372
let mut cmd = cargo_metadata::MetadataCommand::new();
376-
cmd.manifest_path(manifest.path.clone());
373+
cmd.manifest_path(package.manifest_path.clone());
377374
cmd.features(cargo_metadata::CargoOpt::AllFeatures);
378375
cmd.other_options(vec!["--locked".to_string()]);
379376

@@ -402,68 +399,6 @@ fn find_locked_version(
402399
None
403400
}
404401

405-
/// Get all manifests in the workspace.
406-
fn resolve_all(
407-
manifest_path: Option<&Path>,
408-
) -> CargoResult<Vec<(LocalManifest, cargo_metadata::Package)>> {
409-
let mut cmd = cargo_metadata::MetadataCommand::new();
410-
cmd.no_deps();
411-
if let Some(path) = manifest_path {
412-
cmd.manifest_path(path);
413-
}
414-
let result = cmd
415-
.exec()
416-
.with_context(|| "Failed to get workspace metadata")?;
417-
result
418-
.packages
419-
.into_iter()
420-
.map(|package| {
421-
Ok((
422-
LocalManifest::try_new(Path::new(&package.manifest_path))?,
423-
package,
424-
))
425-
})
426-
.collect::<CargoResult<Vec<_>>>()
427-
}
428-
429-
fn resolve_pkgid(
430-
manifest_path: Option<&Path>,
431-
pkgid: &str,
432-
) -> CargoResult<Vec<(LocalManifest, cargo_metadata::Package)>> {
433-
let package = manifest_from_pkgid(manifest_path, pkgid)?;
434-
let manifest = LocalManifest::try_new(Path::new(&package.manifest_path))?;
435-
Ok(vec![(manifest, package)])
436-
}
437-
438-
/// Get the manifest specified by the manifest path. Try to make an educated guess if no path is
439-
/// provided.
440-
fn resolve_local_one(
441-
manifest_path: Option<&Path>,
442-
) -> CargoResult<Vec<(LocalManifest, cargo_metadata::Package)>> {
443-
let resolved_manifest_path: String = find(manifest_path)?.to_string_lossy().into();
444-
445-
let manifest = LocalManifest::find(manifest_path)?;
446-
447-
let mut cmd = cargo_metadata::MetadataCommand::new();
448-
cmd.no_deps();
449-
if let Some(path) = manifest_path {
450-
cmd.manifest_path(path);
451-
}
452-
let result = cmd.exec().with_context(|| "Invalid manifest")?;
453-
let packages = result.packages;
454-
let package = packages
455-
.iter()
456-
.find(|p| p.manifest_path == resolved_manifest_path)
457-
// If we have successfully got metadata, but our manifest path does not correspond to a
458-
// package, we must have been called against a virtual manifest.
459-
.with_context(|| {
460-
"Found virtual manifest, but this command requires running against an \
461-
actual package in this workspace. Try adding `--workspace`."
462-
})?;
463-
464-
Ok(vec![(manifest, package.to_owned())])
465-
}
466-
467402
fn old_version_compatible(old_version_req: &str, new_version: &str) -> bool {
468403
let old_version_req = match VersionReq::parse(old_version_req) {
469404
Ok(req) => req,

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub use dependency::Source;
3434
pub use errors::*;
3535
pub use fetch::{get_latest_dependency, update_registry_index};
3636
pub use manifest::{find, get_dep_version, set_dep_version, LocalManifest, Manifest};
37-
pub use metadata::{manifest_from_pkgid, workspace_members};
37+
pub use metadata::{manifest_from_pkgid, resolve_manifests, workspace_members};
3838
pub use registry::registry_url;
3939
pub use util::{colorize_stderr, shell_print, shell_status, shell_warn, Color, ColorChoice};
4040
pub use version::{upgrade_requirement, VersionExt};

src/manifest.rs

+5-23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{env, str};
66
use semver::Version;
77

88
use super::errors::*;
9+
use super::metadata::find_manifest_path;
910

1011
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
1112
pub enum DepKind {
@@ -69,8 +70,6 @@ impl From<DepKind> for DepTable {
6970
}
7071
}
7172

72-
const MANIFEST_FILENAME: &str = "Cargo.toml";
73-
7473
/// A Cargo manifest
7574
#[derive(Debug, Clone)]
7675
pub struct Manifest {
@@ -423,27 +422,10 @@ pub fn find(specified: Option<&Path>) -> CargoResult<PathBuf> {
423422
{
424423
Ok(path.to_owned())
425424
}
426-
Some(path) => search(path),
427-
None => search(&env::current_dir().with_context(|| "Failed to get current directory")?),
428-
}
429-
}
430-
431-
/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
432-
fn search(dir: &Path) -> CargoResult<PathBuf> {
433-
let mut current_dir = dir;
434-
435-
loop {
436-
let manifest = current_dir.join(MANIFEST_FILENAME);
437-
if fs::metadata(&manifest).is_ok() {
438-
return Ok(manifest);
439-
}
440-
441-
current_dir = match current_dir.parent() {
442-
Some(current_dir) => current_dir,
443-
None => {
444-
anyhow::bail!("Unable to find Cargo.toml for {}", dir.display());
445-
}
446-
};
425+
Some(path) => find_manifest_path(path),
426+
None => find_manifest_path(
427+
&env::current_dir().with_context(|| "Failed to get current directory")?,
428+
),
447429
}
448430
}
449431

src/metadata.rs

+54
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,57 @@ fn canonicalize_path(
5858

5959
path
6060
}
61+
62+
/// Determine packages selected by user
63+
pub fn resolve_manifests(
64+
manifest_path: Option<&Path>,
65+
workspace: bool,
66+
pkgid: Vec<&str>,
67+
) -> CargoResult<Vec<Package>> {
68+
let manifest_path = manifest_path.map(|p| Ok(p.to_owned())).unwrap_or_else(|| {
69+
find_manifest_path(
70+
&std::env::current_dir().with_context(|| "Failed to get current directory")?,
71+
)
72+
})?;
73+
let manifest_path = dunce::canonicalize(manifest_path)?;
74+
75+
let mut cmd = cargo_metadata::MetadataCommand::new();
76+
cmd.no_deps();
77+
cmd.manifest_path(&manifest_path);
78+
let result = cmd.exec().with_context(|| "Invalid manifest")?;
79+
let pkgs = if workspace {
80+
result.packages
81+
} else if !pkgid.is_empty() {
82+
pkgid
83+
.into_iter()
84+
.map(|id| {
85+
result
86+
.packages
87+
.iter()
88+
.find(|pkg| pkg.name == id)
89+
.map(|p| p.to_owned())
90+
.with_context(|| format!("could not find pkgid {}", id))
91+
})
92+
.collect::<Result<Vec<_>, anyhow::Error>>()?
93+
} else {
94+
result
95+
.packages
96+
.iter()
97+
.find(|p| p.manifest_path == manifest_path)
98+
.map(|p| vec![(p.to_owned())])
99+
.unwrap_or_else(|| result.packages)
100+
};
101+
Ok(pkgs)
102+
}
103+
104+
/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
105+
pub(crate) fn find_manifest_path(dir: &Path) -> CargoResult<std::path::PathBuf> {
106+
const MANIFEST_FILENAME: &str = "Cargo.toml";
107+
for path in dir.ancestors() {
108+
let manifest = path.join(MANIFEST_FILENAME);
109+
if std::fs::metadata(&manifest).is_ok() {
110+
return Ok(manifest);
111+
}
112+
}
113+
anyhow::bail!("Unable to find Cargo.toml for {}", dir.display());
114+
}

tests/cmd/upgrade/invalid_manifest.toml

+13-9
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ args = ["upgrade"]
33
status.code = 1
44
stdout = ""
55
stderr = """
6-
Error: Unable to parse Cargo.toml
6+
Error: Invalid manifest
77
88
Caused by:
9-
0: Manifest not valid TOML
10-
1: TOML parse error at line 1, column 6
11-
|
12-
1 | This is clearly not a valid Cargo.toml.
13-
| ^
14-
Unexpected `i`
15-
Expected `.` or `=`
16-
9+
`cargo metadata` exited with an error: error: failed to parse manifest at `[CWD]/Cargo.toml`
10+
11+
Caused by:
12+
could not parse input as TOML
13+
14+
Caused by:
15+
TOML parse error at line 1, column 6
16+
|
17+
1 | This is clearly not a valid Cargo.toml.
18+
| ^
19+
Unexpected `i`
20+
Expected `.` or `=`
1721
"""
1822
fs.sandbox = true
1923

tests/cmd/upgrade/invalid_virtual_manifest.toml

-11
This file was deleted.

tests/cmd/upgrade/invalid_workspace_root_manifest.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ args = ["upgrade", "--workspace"]
33
status.code = 1
44
stdout = ""
55
stderr = """
6-
Error: Failed to get workspace metadata
6+
Error: Invalid manifest
77
88
Caused by:
99
`cargo metadata` exited with an error: error: failed to parse manifest at `[CWD]/Cargo.toml`

0 commit comments

Comments
 (0)