use std::path::PathBuf;
use pmpx_plugin::Family;
use crate::app::Session;
use crate::cli::Cli;
use crate::detect::{self, FamilyScore};
use crate::error::{PmpxError, EXIT_OK};
use crate::style;
pub(super) fn show_detection(args: &Cli) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
let Some(root) = session.project_root().map(PathBuf::from) else {
return Err(session.no_project_error());
};
let selection = session.select(&root).map_err(PmpxError::from)?;
session.emit_notes(&selection);
anstream::println!(
"{} {}",
style::label(12, "Project root"),
style::paint(style::DIM, root.display())
);
anstream::println!(
"{} {}",
style::label(12, "Family"),
selection.family.display()
);
anstream::println!(
"{} {} (score {})",
style::label(12, "Plugin"),
style::paint(style::PM, &selection.name),
selection.score
);
anstream::println!();
anstream::println!(
"{}",
style::paint(
style::DIM,
"Use `pmpx info` to see every candidate and score."
)
);
Ok(EXIT_OK)
}
pub(super) fn show_info(args: &Cli) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
anstream::println!(
"{} {}",
style::label(12, "Start"),
style::paint(style::DIM, session.start_dir.display())
);
match session.project_root() {
Some(root) => anstream::println!(
"{} {}",
style::label(12, "Project root"),
style::paint(style::DIM, root.display())
),
None => anstream::println!("{} (not found)", style::label(12, "Project root")),
}
anstream::println!(
"{} {}, stopped because: {}",
style::label(12, "Walk-up"),
crate::discovery::dirs(session.walk.dirs.len()),
session
.walk
.stopped
.describe(session.global.discovery.max_depth)
);
if session.project.sources.is_empty() {
anstream::println!("{} (none)", style::label(12, "Project config"));
} else {
anstream::println!(
"{} (nearest first, nearest wins)",
style::label(12, "Project config")
);
for p in &session.project.sources {
anstream::println!(" {}", style::paint(style::DIM, p.display()));
}
}
for (family, plugin) in &session.project.plugin {
anstream::println!(
" {} {family} = \"{}\"",
style::label(10, "pinned"),
style::paint(style::PM, plugin)
);
}
anstream::println!();
if session.plugins.plugins.is_empty() {
anstream::println!("{} (none)", style::label(12, "Installed plugins"));
anstream::println!();
anstream::println!(
"{}",
style::paint(style::ERROR_BODY, session.no_project_error())
);
return Ok(EXIT_OK);
}
anstream::println!("Installed plugins");
for p in &session.plugins.plugins {
match p.problem() {
None => anstream::println!(
" {} {:<8} v{}",
style::padded(style::PM, 10, &p.name),
p.family.as_ref().map(Family::as_str).unwrap_or("?"),
style::paint(style::DIM, &p.version)
),
Some(why) => anstream::println!(
" {} ⚠ {}",
style::padded(style::PM, 10, &p.name),
style::paint(style::DIM, why)
),
}
}
anstream::println!();
let Some(root) = session.project_root().map(PathBuf::from) else {
anstream::println!(
"{}",
style::paint(style::DIM, "(no project root, cannot score)")
);
return Ok(EXIT_OK);
};
let families = detect::score_all(&session.plugins, &root, &session.project);
if families.is_empty() {
anstream::println!(
"{} (no plugin can take part in the resolution)",
style::label(12, "Candidates")
);
return Ok(EXIT_OK);
}
anstream::println!("Candidates and scores");
let mut rows: Vec<&FamilyScore> = families.values().collect();
rows.sort_by(|a, b| {
b.score
.cmp(&a.score)
.then_with(|| a.family.as_str().cmp(b.family.as_str()))
});
for fs in rows {
let pin = if fs.pinned {
" [pinned in .pmpx.toml]"
} else {
""
};
anstream::println!(" {} score {}{pin}", fs.family.display(), fs.score);
for p in &fs.plugins {
let hits = p.all_hits().collect::<Vec<_>>().join(", ");
let detail = if hits.is_empty() {
"(no match)".to_string()
} else {
hits
};
anstream::println!(
" {} score {:>4} {}",
style::padded(style::PM, 8, &p.name),
p.score,
style::paint(style::DIM, detail)
);
}
}
anstream::println!();
match session.select_from(&root, &families) {
Ok(selection) => {
session.emit_notes(&selection);
anstream::println!(
"{} {} ({})",
style::label(12, "Selected"),
style::paint(style::PM, &selection.name),
selection.crate_name
);
match session.load_backend(&selection) {
Ok(backend) => {
let d = backend.diagnostics();
anstream::println!(
" {} {}",
style::label(15, "reported name"),
style::paint(style::PM, &d.name)
);
anstream::println!(" {} {}", style::label(15, "reported family"), d.family);
anstream::println!(
" {} {}",
style::label(15, "compiled with"),
style::paint(style::DIM, d.rustc_version)
);
anstream::println!(
" {} {}",
style::label(15, "target"),
style::paint(style::DIM, d.target)
);
if d.family != selection.family.as_str() {
anstream::println!(
" {}",
style::paint(
style::DIM,
format!(
"⚠ the manifest says it is {}, it says it is {} -- was the \
manifest edited?",
selection.family.as_str(),
d.family
)
)
);
}
}
Err(e) => anstream::println!(
" {}",
style::paint(style::DIM, format!("⚠ failed to load: {e}"))
),
}
}
Err(failure) => anstream::println!(
"{} (nothing selected)\n{}",
style::label(12, "Selected"),
style::paint(style::ERROR_BODY, failure.message())
),
}
Ok(EXIT_OK)
}