use std::cmp::Ordering;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::rc::Rc;
use log::debug;
use crate::core::interning::InternedString;
use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
use crate::util::errors::CargoResult;
use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
use crate::core::resolver::{ActivateResult, ResolveOpts};
pub struct RegistryQueryer<'a> {
pub registry: &'a mut (dyn Registry + 'a),
replacements: &'a [(PackageIdSpec, Dependency)],
try_to_use: &'a HashSet<PackageId>,
minimal_versions: bool,
registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
summary_cache: HashMap<
(Option<PackageId>, Summary, ResolveOpts),
Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
>,
used_replacements: HashMap<PackageId, Summary>,
}
impl<'a> RegistryQueryer<'a> {
pub fn new(
registry: &'a mut dyn Registry,
replacements: &'a [(PackageIdSpec, Dependency)],
try_to_use: &'a HashSet<PackageId>,
minimal_versions: bool,
) -> Self {
RegistryQueryer {
registry,
replacements,
try_to_use,
minimal_versions,
registry_cache: HashMap::new(),
summary_cache: HashMap::new(),
used_replacements: HashMap::new(),
}
}
pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
self.used_replacements.get(&p).map(|r| (p, r.package_id()))
}
pub fn replacement_summary(&self, p: PackageId) -> Option<&Summary> {
self.used_replacements.get(&p)
}
pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Summary>>> {
if let Some(out) = self.registry_cache.get(dep).cloned() {
return Ok(out);
}
let mut ret = Vec::new();
self.registry.query(
dep,
&mut |s| {
ret.push(s);
},
false,
)?;
for summary in ret.iter_mut() {
let mut potential_matches = self
.replacements
.iter()
.filter(|&&(ref spec, _)| spec.matches(summary.package_id()));
let &(ref spec, ref dep) = match potential_matches.next() {
None => continue,
Some(replacement) => replacement,
};
debug!(
"found an override for {} {}",
dep.package_name(),
dep.version_req()
);
let mut summaries = self.registry.query_vec(dep, false)?.into_iter();
let s = summaries.next().ok_or_else(|| {
failure::format_err!(
"no matching package for override `{}` found\n\
location searched: {}\n\
version required: {}",
spec,
dep.source_id(),
dep.version_req()
)
})?;
let summaries = summaries.collect::<Vec<_>>();
if !summaries.is_empty() {
let bullets = summaries
.iter()
.map(|s| format!(" * {}", s.package_id()))
.collect::<Vec<_>>();
failure::bail!(
"the replacement specification `{}` matched \
multiple packages:\n * {}\n{}",
spec,
s.package_id(),
bullets.join("\n")
);
}
assert_eq!(s.version(), summary.version());
assert_eq!(s.name(), summary.name());
let replace = if s.source_id() == summary.source_id() {
debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
None
} else {
Some(s)
};
let matched_spec = spec.clone();
if let Some(&(ref spec, _)) = potential_matches.next() {
failure::bail!(
"overlapping replacement specifications found:\n\n \
* {}\n * {}\n\nboth specifications match: {}",
matched_spec,
spec,
summary.package_id()
);
}
for dep in summary.dependencies() {
debug!("\t{} => {}", dep.package_name(), dep.version_req());
}
if let Some(r) = replace {
self.used_replacements.insert(summary.package_id(), r);
}
}
ret.sort_unstable_by(|a, b| {
let a_in_previous = self.try_to_use.contains(&a.package_id());
let b_in_previous = self.try_to_use.contains(&b.package_id());
let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
match previous_cmp {
Ordering::Equal => {
let cmp = a.version().cmp(b.version());
if self.minimal_versions {
cmp
} else {
cmp.reverse()
}
}
_ => previous_cmp,
}
});
let out = Rc::new(ret);
self.registry_cache.insert(dep.clone(), out.clone());
Ok(out)
}
pub fn build_deps(
&mut self,
parent: Option<PackageId>,
candidate: &Summary,
opts: &ResolveOpts,
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
if let Some(out) = self
.summary_cache
.get(&(parent, candidate.clone(), opts.clone()))
.cloned()
{
return Ok(out);
}
let (used_features, deps) = resolve_features(parent, candidate, opts)?;
let mut deps = deps
.into_iter()
.map(|(dep, features)| {
let candidates = self.query(&dep)?;
Ok((dep, candidates, features))
})
.collect::<CargoResult<Vec<DepInfo>>>()?;
deps.sort_by_key(|&(_, ref a, _)| a.len());
let out = Rc::new((used_features, Rc::new(deps)));
self.summary_cache
.insert((parent, candidate.clone(), opts.clone()), out.clone());
Ok(out)
}
}
pub fn resolve_features<'b>(
parent: Option<PackageId>,
s: &'b Summary,
opts: &'b ResolveOpts,
) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
let deps = s.dependencies();
let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
let reqs = build_requirements(s, opts)?;
let mut ret = Vec::new();
let mut used_features = HashSet::new();
let default_dep = (false, BTreeSet::new());
for dep in deps {
if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) {
continue;
}
let base = reqs.deps.get(&dep.name_in_toml()).unwrap_or(&default_dep);
used_features.insert(dep.name_in_toml());
let always_required = !dep.is_optional()
&& !s
.dependencies()
.iter()
.any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
if always_required && base.0 {
return Err(match parent {
None => failure::format_err!(
"Package `{}` does not have feature `{}`. It has a required dependency \
with that name, but only optional dependencies can be used as features.",
s.package_id(),
dep.name_in_toml()
)
.into(),
Some(p) => (
p,
ConflictReason::RequiredDependencyAsFeatures(dep.name_in_toml()),
)
.into(),
});
}
let mut base = base.1.clone();
base.extend(dep.features().iter());
for feature in base.iter() {
if feature.contains('/') {
return Err(failure::format_err!(
"feature names may not contain slashes: `{}`",
feature
)
.into());
}
}
ret.push((dep.clone(), Rc::new(base)));
}
let remaining = reqs
.deps
.keys()
.cloned()
.filter(|s| !used_features.contains(s))
.collect::<Vec<_>>();
if !remaining.is_empty() {
let features = remaining.join(", ");
return Err(match parent {
None => failure::format_err!(
"Package `{}` does not have these features: `{}`",
s.package_id(),
features
)
.into(),
Some(p) => (p, ConflictReason::MissingFeatures(features)).into(),
});
}
Ok((reqs.into_used(), ret))
}
fn build_requirements<'a, 'b: 'a>(
s: &'a Summary,
opts: &'b ResolveOpts,
) -> CargoResult<Requirements<'a>> {
let mut reqs = Requirements::new(s);
if opts.all_features {
for key in s.features().keys() {
reqs.require_feature(*key)?;
}
for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
reqs.require_dependency(dep.name_in_toml());
}
} else {
for &f in opts.features.iter() {
reqs.require_value(&FeatureValue::new(f, s))?;
}
}
if opts.uses_default_features && s.features().contains_key("default") {
reqs.require_feature(InternedString::new("default"))?;
}
Ok(reqs)
}
struct Requirements<'a> {
summary: &'a Summary,
deps: HashMap<InternedString, (bool, BTreeSet<InternedString>)>,
used: HashSet<InternedString>,
visited: HashSet<InternedString>,
}
impl Requirements<'_> {
fn new(summary: &Summary) -> Requirements<'_> {
Requirements {
summary,
deps: HashMap::new(),
used: HashSet::new(),
visited: HashSet::new(),
}
}
fn into_used(self) -> HashSet<InternedString> {
self.used
}
fn require_crate_feature(&mut self, package: InternedString, feat: InternedString) {
if let Some(dep) = self
.summary
.dependencies()
.iter()
.find(|p| p.name_in_toml() == package)
{
if dep.is_optional() {
self.used.insert(package);
}
}
self.deps
.entry(package)
.or_insert((false, BTreeSet::new()))
.1
.insert(feat);
}
fn seen(&mut self, feat: InternedString) -> bool {
if self.visited.insert(feat) {
self.used.insert(feat);
false
} else {
true
}
}
fn require_dependency(&mut self, pkg: InternedString) {
if self.seen(pkg) {
return;
}
self.deps.entry(pkg).or_insert((false, BTreeSet::new())).0 = true;
}
fn require_feature(&mut self, feat: InternedString) -> CargoResult<()> {
if feat.is_empty() || self.seen(feat) {
return Ok(());
}
for fv in self
.summary
.features()
.get(&feat)
.expect("must be a valid feature")
{
match *fv {
FeatureValue::Feature(ref dep_feat) if **dep_feat == *feat => failure::bail!(
"cyclic feature dependency: feature `{}` depends on itself",
feat
),
_ => {}
}
self.require_value(fv)?;
}
Ok(())
}
fn require_value(&mut self, fv: &FeatureValue) -> CargoResult<()> {
match fv {
FeatureValue::Feature(feat) => self.require_feature(*feat)?,
FeatureValue::Crate(dep) => self.require_dependency(*dep),
FeatureValue::CrateFeature(dep, dep_feat) => {
self.require_crate_feature(*dep, *dep_feat)
}
};
Ok(())
}
}