#![cfg_attr(coverage_nightly, coverage(off))]
use super::types::{DepCategory, DepsAuditReport, ParetoEffort, ParetoEntry};
use crate::cli::colors;
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn print_pareto_report(entries: &[ParetoEntry]) {
println!("{}", colors::rule());
println!(
"{}π Pareto Analysis: 80/20 Dependency Removal{}",
colors::BOLD,
colors::RESET
);
println!("{}", colors::rule());
println!();
println!(
"{}ROI = Transitive Deps Saved / Effort{}",
colors::DIM,
colors::RESET
);
println!(
"{}Higher ROI = Better bang for buck{}",
colors::DIM,
colors::RESET
);
println!();
if entries.is_empty() {
println!("No removable dependencies found.");
return;
}
let total_transitive: usize = entries.iter().map(|e| e.transitive_deps).sum();
let target_80 = (total_transitive as f32 * 0.8) as usize;
println!("βββββββββββββββββββββββ¬ββββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββββββββββββββββββββ");
println!("β Dependency β Trans.Depsβ Effort β ROI β Reason β");
println!("βββββββββββββββββββββββΌββββββββββββΌβββββββββΌβββββββββΌββββββββββββββββββββββββββββββ€");
let mut cumulative = 0;
let mut marked_80 = false;
for entry in entries.iter().take(20) {
cumulative += entry.transitive_deps;
let marker = if !marked_80 && cumulative >= target_80 {
marked_80 = true;
"β 80%"
} else {
""
};
let name_str = entry
.name
.get(..entry.name.len().min(19))
.unwrap_or(&entry.name);
let reason_str = entry
.reason
.get(..entry.reason.len().min(21))
.unwrap_or(&entry.reason);
println!(
"β {}{:<19}{} β {:>9} β {:>6} β {}{:>6.1}{} β {:<21} {:>5} β",
colors::CYAN,
name_str,
colors::RESET,
entry.transitive_deps,
entry.effort.label(),
colors::BOLD,
entry.roi,
colors::RESET,
reason_str,
marker
);
}
println!("βββββββββββββββββββββββ΄ββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββββββββββββββββββββ");
println!();
let top_5_savings: usize = entries.iter().take(5).map(|e| e.transitive_deps).sum();
let top_5_pct = if total_transitive > 0 {
(top_5_savings as f32 / total_transitive as f32 * 100.0) as usize
} else {
0
};
println!("{}π‘ Summary:{}", colors::BOLD, colors::RESET);
println!(
" Total transitive deps from candidates: {}{}{}",
colors::BOLD_WHITE,
total_transitive,
colors::RESET
);
println!(
" Top 5 removals save: {}{}{} deps ({}{}{}% of total)",
colors::BOLD_WHITE,
top_5_savings,
colors::RESET,
colors::BOLD_WHITE,
top_5_pct,
colors::RESET
);
println!();
println!(
"{}π§ Quick Wins (Low Effort, High ROI):{}",
colors::BOLD,
colors::RESET
);
for entry in entries
.iter()
.filter(|e| matches!(e.effort, ParetoEffort::Low) && e.roi > 10.0)
.take(5)
{
println!(
" {}cargo rm {}{} # saves {}{}{} transitive deps",
colors::GREEN,
entry.name,
colors::RESET,
colors::BOLD_WHITE,
entry.transitive_deps,
colors::RESET
);
}
println!();
println!("{}", colors::rule());
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn print_text_report(report: &DepsAuditReport) {
println!("{}", colors::rule());
println!(
"{}π Dependency Audit Report (with Graph Analysis){}",
colors::BOLD,
colors::RESET
);
println!("{}", colors::rule());
println!();
println!("{}π Summary{}", colors::BOLD, colors::RESET);
println!(
" Direct Dependencies: {}{}{}",
colors::BOLD_WHITE,
report.direct_deps,
colors::RESET
);
println!(
" Transitive Deps: {}{}{}",
colors::BOLD_WHITE,
report.transitive_deps,
colors::RESET
);
println!(
" Total (graph nodes): {}{}{}",
colors::BOLD_WHITE,
report.total_deps,
colors::RESET
);
println!(
" Sovereign Stack: {}{}{} β
",
colors::GREEN,
report.sovereign_deps,
colors::RESET
);
println!(
" Replaceable: {}{}{} π",
colors::YELLOW,
report.replaceable_deps,
colors::RESET
);
println!(
" Removable: {}{}{} β",
colors::RED,
report.removable_deps,
colors::RESET
);
println!(
" Heavy (bloat): {}{}{} β οΈ",
colors::YELLOW,
report.heavy_deps,
colors::RESET
);
println!(
" Orphans (easy remove): {}{}{} π―",
colors::BOLD_WHITE,
report.orphan_deps,
colors::RESET
);
println!(
" Bridges (connectors): {}{}{} π",
colors::BOLD_WHITE,
report.bridge_deps,
colors::RESET
);
let savings_color = if report.estimated_savings_kb > 0 {
colors::YELLOW
} else {
colors::BOLD_WHITE
};
println!(
" Est. Savings: {}~{}KB (~{}MB){}",
savings_color,
report.estimated_savings_kb,
report.estimated_savings_kb / 1024,
colors::RESET
);
println!();
if !report.top_critical.is_empty() {
println!(
"{}π Critical Dependencies (by PageRank){}",
colors::BOLD,
colors::RESET
);
println!(" βββββββββββββββββββββββ¬βββββββββββ");
println!(" β Dependency β Score β");
println!(" βββββββββββββββββββββββΌβββββββββββ€");
for (name, score) in report.top_critical.iter().take(5) {
let name_str = name.get(..name.len().min(19)).unwrap_or(name);
println!(
" β {}{:<19}{} β {}{:.6}{} β",
colors::CYAN,
name_str,
colors::RESET,
colors::BOLD,
score,
colors::RESET
);
}
println!(" βββββββββββββββββββββββ΄βββββββββββ");
println!(
" {}(Higher = more deps depend on it, harder to remove){}",
colors::DIM,
colors::RESET
);
println!();
}
let removable: Vec<_> = report
.dependencies
.iter()
.filter(|d| d.category == DepCategory::Removable)
.collect();
let heavy: Vec<_> = report
.dependencies
.iter()
.filter(|d| d.category == DepCategory::Heavy)
.collect();
let replaceable: Vec<_> = report
.dependencies
.iter()
.filter(|d| d.category == DepCategory::Replaceable)
.collect();
let dev_only: Vec<_> = report
.dependencies
.iter()
.filter(|d| d.category == DepCategory::DevOnly)
.collect();
if !removable.is_empty() {
println!(
"{}β Removable Dependencies{}",
colors::BOLD_RED,
colors::RESET
);
println!(" βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ");
println!(" β Dependency β Reason β");
println!(" βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ€");
for dep in &removable {
let name_str = dep.name.get(..dep.name.len().min(19)).unwrap_or(&dep.name);
let reason_str = dep
.reason
.get(..dep.reason.len().min(38))
.unwrap_or(&dep.reason);
println!(
" β {}{:<19}{} β {:<38} β",
colors::CYAN,
name_str,
colors::RESET,
reason_str
);
}
println!(" βββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββ");
println!();
}
if !heavy.is_empty() {
println!(
"{}β οΈ Heavy Dependencies (Bloat){}",
colors::BOLD_YELLOW,
colors::RESET
);
println!(" βββββββββββββββββββββββ¬βββββββββββ¬ββββββββββββββββββββββββββββββ");
println!(" β Dependency β Size KB β Reason β");
println!(" βββββββββββββββββββββββΌβββββββββββΌββββββββββββββββββββββββββββββ€");
for dep in &heavy {
let name_str = dep.name.get(..dep.name.len().min(19)).unwrap_or(&dep.name);
let reason_str = dep
.reason
.get(..dep.reason.len().min(27))
.unwrap_or(&dep.reason);
println!(
" β {}{:<19}{} β {:>8} β {:<27} β",
colors::CYAN,
name_str,
colors::RESET,
dep.estimated_size_kb,
reason_str
);
}
println!(" βββββββββββββββββββββββ΄βββββββββββ΄ββββββββββββββββββββββββββββββ");
println!();
}
if !replaceable.is_empty() {
println!(
"{}π Replaceable with Sovereign Stack{}",
colors::BOLD_YELLOW,
colors::RESET
);
println!(" βββββββββββββββββββββββ¬ββββββββββββββββββββββ¬ββββββββββββββββββββ");
println!(" β Dependency β Replacement β Benefit β");
println!(" βββββββββββββββββββββββΌββββββββββββββββββββββΌββββββββββββββββββββ€");
for dep in &replaceable {
let replacement = dep.replacement.as_deref().unwrap_or("-");
let name_str = dep.name.get(..dep.name.len().min(19)).unwrap_or(&dep.name);
let repl_str = replacement
.get(..replacement.len().min(19))
.unwrap_or(replacement);
let reason_str = dep
.reason
.get(..dep.reason.len().min(17))
.unwrap_or(&dep.reason);
println!(
" β {}{:<19}{} β {}{:<19}{} β {:<17} β",
colors::CYAN,
name_str,
colors::RESET,
colors::GREEN,
repl_str,
colors::RESET,
reason_str
);
}
println!(" βββββββββββββββββββββββ΄ββββββββββββββββββββββ΄ββββββββββββββββββββ");
println!();
}
if !dev_only.is_empty() {
println!(
"{}π§ͺ Dev-Only Dependencies ({}){}",
colors::BOLD,
dev_only.len(),
colors::RESET
);
let names: Vec<_> = dev_only.iter().map(|d| d.name.as_str()).collect();
println!(" {}", names.join(", "));
println!();
}
if !report.recommendations.is_empty() {
println!("{}π‘ Recommendations{}", colors::BOLD, colors::RESET);
for (i, rec) in report.recommendations.iter().enumerate() {
println!(" {}. {}", i + 1, rec);
}
println!();
}
println!("{}", colors::rule());
println!(
"{}Run with --all to see Core and Sovereign deps{}",
colors::DIM,
colors::RESET
);
}