allow each source to recommend packages that are close to a dependency

This commit is contained in:
Eh2406 2018-07-11 12:46:52 -04:00
parent 8a717d32db
commit 84cc3d8b09
12 changed files with 110 additions and 30 deletions

View file

@ -14,11 +14,11 @@ use sources::config::SourceConfigMap;
/// See also `core::Source`.
pub trait Registry {
/// Attempt to find the packages that match a dependency request.
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()>;
fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Vec<Summary>> {
let mut ret = Vec::new();
self.query(dep, &mut |s| ret.push(s))?;
self.query(dep, &mut |s| ret.push(s), fuzzy)?;
Ok(ret)
}
}
@ -395,7 +395,7 @@ http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
}
impl<'cfg> Registry for PackageRegistry<'cfg> {
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()> {
assert!(self.patches_locked);
let (override_summary, n, to_warn) = {
// Look for an override and get ready to query the real source.
@ -476,7 +476,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
// already selected, then we skip this `summary`.
let locked = &self.locked;
let all_patches = &self.patches_available;
return source.query(dep, &mut |summary| {
let callback = &mut |summary: Summary| {
for patch in patches.iter() {
let patch = patch.package_id().version();
if summary.package_id().version() == patch {
@ -484,7 +484,12 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
}
}
f(lock(locked, all_patches, summary))
});
};
return if fuzzy {
source.fuzzy_query(dep, callback)
} else {
source.query(dep, callback)
};
}
// If we have an override summary then we query the source
@ -496,10 +501,17 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
}
let mut n = 0;
let mut to_warn = None;
source.query(dep, &mut |summary| {
n += 1;
to_warn = Some(summary);
})?;
{
let callback = &mut |summary| {
n += 1;
to_warn = Some(summary);
};
if fuzzy {
source.fuzzy_query(dep, callback)?;
} else {
source.query(dep, callback)?;
}
}
(override_summary, n, to_warn)
}
}

View file

@ -922,17 +922,16 @@ fn activation_error(
return format_err!("{}", msg);
}
// Once we're all the way down here, we're definitely lost in the
// weeds! We didn't actually find any candidates, so we need to
// We didn't actually find any candidates, so we need to
// give an error message that nothing was found.
//
// Note that we re-query the registry with a new dependency that
// allows any version so we can give some nicer error reporting
// which indicates a few versions that were actually found.
// Maybe the user mistyped the ver_req? Like `dep="2"` when `dep=".2"`
// was meant. So we re-query the registry with `deb="*"` so we can
// list a few versions that were actually found.
let all_req = semver::VersionReq::parse("*").unwrap();
let mut new_dep = dep.clone();
new_dep.set_version_req(all_req);
let mut candidates = match registry.query_vec(&new_dep) {
let mut candidates = match registry.query_vec(&new_dep, false) {
Ok(candidates) => candidates,
Err(e) => return e,
};
@ -977,12 +976,25 @@ fn activation_error(
msg
} else {
// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
// was meant. So we try asking the registry for a `fuzzy` search for suggestions.
let mut candidates = Vec::new();
if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.name()), true) {
return e
};
candidates.sort_unstable();
candidates.dedup();
let mut msg = format!(
"no matching package named `{}` found\n\
location searched: {}\n",
dep.name(),
dep.source_id()
);
if !candidates.is_empty() {
msg.push_str("did you mean: ");
msg.push_str(&candidates.join(" or "));
msg.push_str("\n");
}
msg.push_str("required by ");
msg.push_str(&describe_path(&graph.path_to_top(parent.package_id())));

View file

@ -52,7 +52,7 @@ impl<'a> RegistryQueryer<'a> {
summary: s,
replace: None,
});
})?;
}, false)?;
for candidate in ret.iter_mut() {
let summary = &candidate.summary;
@ -66,7 +66,7 @@ impl<'a> RegistryQueryer<'a> {
};
debug!("found an override for {} {}", dep.name(), dep.version_req());
let mut summaries = self.registry.query_vec(dep)?.into_iter();
let mut summaries = self.registry.query_vec(dep, false)?.into_iter();
let s = summaries.next().ok_or_else(|| {
format_err!(
"no matching package for override `{}` found\n\

View file

@ -25,6 +25,12 @@ pub trait Source {
/// Attempt to find the packages that match a dependency request.
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
/// Attempt to find the packages that are close to a dependency request.
/// Each source gets to define what `close` means for it.
/// path/git sources may return all dependencies that are at that uri.
/// where as an Index source may return dependencies that have the same canonicalization.
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
let mut ret = Vec::new();
self.query(dep, &mut |s| ret.push(s))?;
@ -79,6 +85,11 @@ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
(**self).query(dep, f)
}
/// Forwards to `Source::query`
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
(**self).fuzzy_query(dep, f)
}
/// Forwards to `Source::source_id`
fn source_id(&self) -> &SourceId {
(**self).source_id()

View file

@ -54,6 +54,14 @@ impl<'cfg> Source for DirectorySource<'cfg> {
Ok(())
}
fn fuzzy_query(&mut self, _dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
let packages = self.packages.values().map(|p| &p.0);
for summary in packages.map(|pkg| pkg.summary().clone()) {
f(summary);
}
Ok(())
}
fn supports_checksums(&self) -> bool {
true
}

View file

@ -130,6 +130,13 @@ impl<'cfg> Source for GitSource<'cfg> {
src.query(dep, f)
}
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
let src = self.path_source
.as_mut()
.expect("BUG: update() must be called before query()");
src.fuzzy_query(dep, f)
}
fn supports_checksums(&self) -> bool {
false
}

View file

@ -508,6 +508,13 @@ impl<'cfg> Source for PathSource<'cfg> {
Ok(())
}
fn fuzzy_query(&mut self, _dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
for s in self.packages.iter().map(|p| p.summary()) {
f(s.clone())
}
Ok(())
}
fn supports_checksums(&self) -> bool {
false
}

View file

@ -208,7 +208,7 @@ impl<'cfg> RegistryIndex<'cfg> {
Ok((summary, yanked.unwrap_or(false)))
}
pub fn query(
pub fn query_inner(
&mut self,
dep: &Dependency,
load: &mut RegistryData,
@ -242,9 +242,7 @@ impl<'cfg> RegistryIndex<'cfg> {
});
for summary in summaries {
if dep.matches(&summary) {
f(summary);
}
f(summary);
}
Ok(())
}

View file

@ -463,9 +463,11 @@ impl<'cfg> Source for RegistrySource<'cfg> {
if dep.source_id().precise().is_some() && !self.updated {
debug!("attempting query without update");
let mut called = false;
self.index.query(dep, &mut *self.ops, &mut |s| {
called = true;
f(s);
self.index.query_inner(dep, &mut *self.ops, &mut |s| {
if dep.matches(&s) {
called = true;
f(s);
}
})?;
if called {
return Ok(());
@ -475,7 +477,15 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
}
self.index.query(dep, &mut *self.ops, f)
self.index.query_inner(dep, &mut *self.ops, &mut |s| {
if dep.matches(&s) {
f(s);
}
})
}
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
self.index.query_inner(dep, &mut *self.ops, f)
}
fn supports_checksums(&self) -> bool {

View file

@ -35,6 +35,19 @@ impl<'cfg> Source for ReplacedSource<'cfg> {
Ok(())
}
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
let (replace_with, to_replace) = (&self.replace_with, &self.to_replace);
let dep = dep.clone().map_source(to_replace, replace_with);
self.inner
.fuzzy_query(
&dep,
&mut |summary| f(summary.map_source(replace_with, to_replace)),
)
.chain_err(|| format!("failed to query replaced source {}", self.to_replace))?;
Ok(())
}
fn supports_checksums(&self) -> bool {
self.inner.supports_checksums()
}

View file

@ -161,7 +161,7 @@ fn wrong_case() {
.file("src/main.rs", "fn main() {}")
.build();
// TODO: #5678 to make this work or at least give better error message
// #5678 to make this work
assert_that(
p.cargo("build"),
execs().with_status(101).with_stderr(
@ -169,6 +169,7 @@ fn wrong_case() {
[UPDATING] registry [..]
error: no matching package named `Init` found
location searched: registry [..]
did you mean: init
required by package `foo v0.0.1 ([..])`
",
),
@ -195,7 +196,7 @@ fn mis_hyphenated() {
.file("src/main.rs", "fn main() {}")
.build();
// TODO: #2775 to make this work or at least give better error message
// #2775 to make this work
assert_that(
p.cargo("build"),
execs().with_status(101).with_stderr(
@ -203,6 +204,7 @@ fn mis_hyphenated() {
[UPDATING] registry [..]
error: no matching package named `mis_hyphenated` found
location searched: registry [..]
did you mean: mis-hyphenated
required by package `foo v0.0.1 ([..])`
",
),

View file

@ -28,9 +28,9 @@ fn resolve_with_config(
) -> CargoResult<Vec<PackageId>> {
struct MyRegistry<'a>(&'a [Summary]);
impl<'a> Registry for MyRegistry<'a> {
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()> {
for summary in self.0.iter() {
if dep.matches(summary) {
if fuzzy || dep.matches(summary) {
f(summary.clone());
}
}