use aube_lockfile::LockfileGraph;
use aube_settings::resolved::DeprecationWarnings;
use clx::style;
use std::collections::{BTreeMap, BTreeSet};
use std::io::Write;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct DeprecationRecord {
pub name: String,
pub version: String,
pub dep_path: String,
pub message: Arc<str>,
}
pub fn classify<'a>(
records: &'a [DeprecationRecord],
graph: &LockfileGraph,
) -> (Vec<&'a DeprecationRecord>, Vec<&'a DeprecationRecord>) {
let direct_keys: BTreeSet<(&str, &str)> = graph
.importers
.values()
.flat_map(|deps| deps.iter())
.filter_map(|d| graph.packages.get(&d.dep_path))
.map(|pkg| (pkg.name.as_str(), pkg.version.as_str()))
.collect();
let mut direct = Vec::new();
let mut transitive = Vec::new();
for r in records {
if direct_keys.contains(&(r.name.as_str(), r.version.as_str())) {
direct.push(r);
} else {
transitive.push(r);
}
}
(direct, transitive)
}
pub fn retain_in_graph(records: &mut Vec<DeprecationRecord>, graph: &LockfileGraph) {
let present: BTreeSet<(&str, &str)> = graph
.packages
.values()
.map(|p| (p.name.as_str(), p.version.as_str()))
.collect();
records.retain(|r| present.contains(&(r.name.as_str(), r.version.as_str())));
}
pub fn dedupe(records: Vec<DeprecationRecord>) -> Vec<DeprecationRecord> {
let mut seen: BTreeMap<(String, String), DeprecationRecord> = BTreeMap::new();
for r in records {
seen.entry((r.name.clone(), r.version.clone())).or_insert(r);
}
seen.into_values().collect()
}
pub fn render_install_warnings(
records: &[DeprecationRecord],
graph: &LockfileGraph,
mode: DeprecationWarnings,
) {
if records.is_empty() {
return;
}
let (direct, transitive) = classify(records, graph);
match mode {
DeprecationWarnings::None => {}
DeprecationWarnings::Summary => write_count_line(records.len(), !transitive.is_empty()),
DeprecationWarnings::Direct => {
for r in &direct {
write_warn_line(r);
}
if !transitive.is_empty() {
write_transitive_count_line(transitive.len());
}
}
DeprecationWarnings::All => {
for r in direct.iter().chain(transitive.iter()) {
write_warn_line(r);
}
}
}
}
fn write_warn_line(r: &DeprecationRecord) {
let line = format!(
"{} {}@{}: {}",
style::eyellow("WARN deprecated").bold(),
r.name,
r.version,
r.message
);
let _ = writeln!(std::io::stderr(), "{line}");
}
fn write_transitive_count_line(count: usize) {
let pkgs = pluralizer::pluralize("transitive package", count as isize, true);
let verb = if count == 1 { "has" } else { "have" };
let msg = format!(
"{pkgs} {verb} deprecation warnings. Run `aube deprecations --transitive` to see them."
);
let _ = writeln!(std::io::stderr(), "{}", style::edim(msg));
}
fn write_count_line(count: usize, has_transitive: bool) {
let pkgs = pluralizer::pluralize("package", count as isize, true);
let verb = if count == 1 { "has" } else { "have" };
let cmd = if has_transitive {
"aube deprecations --transitive"
} else {
"aube deprecations"
};
let msg = format!("{pkgs} {verb} deprecation warnings. Run `{cmd}` to see them.");
let _ = writeln!(std::io::stderr(), "{}", style::edim(msg));
}