use std::collections::BTreeMap;
use std::path::Path;
use pmpx_plugin::Family;
use crate::config::MergedProjectConfig;
use crate::plugins::{InstalledPlugin, PluginSet};
mod failure;
mod resolve;
pub use failure::DetectFailure;
pub use resolve::{select, select_from_scores, Selection};
pub const STRONG_SCORE: u32 = 100;
pub const WEAK_SCORE: u32 = 10;
pub const PIN_FLOOR: u32 = 50;
const _: () = assert!(PIN_FLOOR > WEAK_SCORE);
const _: () = assert!(PIN_FLOOR < STRONG_SCORE);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScoredPlugin {
pub crate_name: String,
pub name: String,
pub strong_hits: Vec<String>,
pub weak_hits: Vec<String>,
pub score: u32,
}
impl ScoredPlugin {
pub fn score(plugin: &InstalledPlugin, dir: &Path) -> Self {
let strong_hits = hits(dir, &plugin.strong);
let weak_hits = hits(dir, &plugin.weak);
let score = STRONG_SCORE * strong_hits.len() as u32 + WEAK_SCORE * weak_hits.len() as u32;
Self {
crate_name: plugin.crate_name.clone(),
name: plugin.name.clone(),
strong_hits,
weak_hits,
score,
}
}
pub fn all_hits(&self) -> impl Iterator<Item = &str> {
self.strong_hits
.iter()
.chain(self.weak_hits.iter())
.map(String::as_str)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FamilyScore {
pub family: Family,
pub score: u32,
pub pinned: bool,
pub plugins: Vec<ScoredPlugin>,
}
fn hits(dir: &Path, names: &[String]) -> Vec<String> {
let mut out: Vec<String> = names
.iter()
.filter(|n| dir.join(n.as_str()).exists())
.cloned()
.collect();
out.sort();
out
}
pub fn score_all(
set: &PluginSet,
root: &Path,
merged: &MergedProjectConfig,
) -> BTreeMap<Family, FamilyScore> {
let mut families: BTreeMap<Family, FamilyScore> = BTreeMap::new();
for plugin in set.usable() {
let Some(family) = plugin.family.clone() else {
continue;
};
let scored = ScoredPlugin::score(plugin, root);
let entry = families
.entry(family.clone())
.or_insert_with(|| FamilyScore {
family,
score: 0,
pinned: false,
plugins: Vec::new(),
});
entry.score = entry.score.max(scored.score);
entry.plugins.push(scored);
}
for (family, fs) in families.iter_mut() {
if merged.pinned_plugin(family.as_str()).is_some() {
fs.pinned = true;
fs.score = fs.score.max(PIN_FLOOR);
}
}
for name in merged.pinned_families() {
let family = Family::new(name.to_string());
families
.entry(family.clone())
.or_insert_with(|| FamilyScore {
family,
score: PIN_FLOOR,
pinned: true,
plugins: Vec::new(),
});
}
for fs in families.values_mut() {
fs.plugins.sort_by(|a, b| a.crate_name.cmp(&b.crate_name));
}
families
}
#[cfg(test)]
mod tests;