use std::collections::{BTreeMap, BTreeSet};
pub type BuildIndexCache =
Box<dyn FnOnce(BTreeSet<String>) -> BTreeMap<String, Option<IndexKrate>>>;
pub type FeaturesMap = BTreeMap<String, Vec<String>>;
#[derive(Clone)]
pub struct IndexKrateVersion {
pub version: semver::Version,
pub features: FeaturesMap,
}
#[derive(Clone)]
pub struct IndexKrate {
pub versions: Vec<IndexKrateVersion>,
}
pub struct CachingIndex {
cache: BTreeMap<String, Option<IndexKrate>>,
}
impl CachingIndex {
#[inline]
pub fn new(build: BuildIndexCache, krates: BTreeSet<String>) -> Self {
Self {
cache: build(krates),
}
}
fn index_krate_features(&self, name: &str, version: &semver::Version) -> Option<&FeaturesMap> {
self.cache.get(name).and_then(|ik| {
ik.as_ref().and_then(|ik| {
ik.versions
.iter()
.find_map(|ikv| (&ikv.version == version).then_some(&ikv.features))
})
})
}
}
pub(super) fn fix_features(index: &CachingIndex, krate: &mut crate::Package) {
if krate
.source
.as_ref()
.map_or(true, |src| !src.is_crates_io())
{
return;
}
let Some(features) = index.index_krate_features(&krate.name, &krate.version) else {
return;
};
for (ikey, ivalue) in features {
if !krate.features.contains_key(ikey) {
krate.features.insert(ikey.clone(), ivalue.clone());
}
}
let missing_deps: Vec<_> = krate
.features
.iter()
.flat_map(|(_, sf)| sf.iter())
.filter_map(|sf| {
let pf = crate::ParsedFeature::from(sf.as_str());
if let super::features::Feature::Simple(simple) = pf.feat() {
if krate.features.contains_key(simple) {
None
} else {
Some(simple.to_owned())
}
} else {
None
}
})
.collect();
for missing in missing_deps {
let dep_feature = format!("dep:{missing}");
krate.features.insert(missing, vec![dep_feature]);
}
}