use pmpx_plugin::Family;
use crate::app::Session;
use crate::cli::Cli;
use crate::error::{PmpxError, EXIT_OK};
use crate::runtime::Backend;
use crate::style;
pub(super) fn plugin_add(
args: &Cli,
names: &[String],
version: Option<&str>,
) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
if version.is_some() && names.len() > 1 {
return Err(PmpxError::Usage(
"--version can only be used with a single plugin name".to_string(),
));
}
for name in names {
anstream::println!("Installing {}...", style::paint(style::PM, name));
let installed = session.kit.install(name, version)?;
anstream::println!(
" {} v{} ({}) -> {}",
installed.crate_name,
style::paint(style::DIM, installed.version),
style::paint(style::DIM, describe_source(installed.source)),
style::paint(style::DIM, installed.dir.display())
);
}
Ok(EXIT_OK)
}
pub(super) fn plugin_rm(args: &Cli, names: &[String]) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
for name in names {
session.kit.uninstall(name)?;
anstream::println!("Removed {}", style::paint(style::PM, name));
}
Ok(EXIT_OK)
}
pub(super) fn plugin_update(
args: &Cli,
names: &[String],
version: Option<&str>,
) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
if version.is_some() && names.len() > 1 {
return Err(PmpxError::Usage(
"--version can only be used with a single plugin name".to_string(),
));
}
let targets: Vec<String> = if names.is_empty() {
session.plugins.usable().map(|p| p.name.clone()).collect()
} else {
names.to_vec()
};
if targets.is_empty() {
anstream::println!("No plugins to update.");
return Ok(EXIT_OK);
}
for name in targets {
let installed = session.kit.update(&name, version)?;
anstream::println!(
"{} -> v{}",
installed.crate_name,
style::paint(style::DIM, installed.version)
);
}
Ok(EXIT_OK)
}
pub(super) fn plugin_search(args: &Cli, keyword: &str, limit: usize) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
let results = session.kit.search(keyword, limit)?;
if results.is_empty() {
anstream::println!("crates.io has no crate matching {keyword:?}.");
return Ok(EXIT_OK);
}
for r in results {
anstream::println!(
"{:<32} v{} ↓{}",
r.name,
style::padded(style::DIM, 10, &r.version),
r.downloads
);
if let Some(d) = r.description {
anstream::println!(" {}", style::paint(style::DIM, d));
}
}
Ok(EXIT_OK)
}
pub(super) fn plugin_info(args: &Cli, name: &str) -> crate::error::Result<u8> {
let session = Session::open(args).map_err(PmpxError::Other)?;
let mut found = false;
if let Some(p) = session.plugins.by_name(name) {
found = true;
anstream::println!("Installed");
anstream::println!(
" {} {}",
style::label(15, "reported name"),
style::paint(style::PM, &p.name)
);
anstream::println!(" {} {}", style::label(15, "crate"), p.crate_name);
anstream::println!(
" {} {}",
style::label(15, "version"),
style::paint(style::DIM, &p.version)
);
anstream::println!(
" {} {}",
style::label(15, "family"),
p.family
.as_ref()
.map(Family::as_str)
.unwrap_or("(not declared)")
);
anstream::println!(
" {} {}",
style::label(15, "ABI"),
p.abi
.map(|a| a.to_string())
.unwrap_or_else(|| "(not declared)".into())
);
anstream::println!(
" {} {}",
style::label(15, "directory"),
style::paint(style::DIM, p.dir.display())
);
anstream::println!(
" {} {}",
style::label(15, "strong evidence"),
style::paint(style::DIM, join_or_dash(&p.strong))
);
anstream::println!(
" {} {}",
style::label(15, "weak evidence"),
style::paint(style::DIM, join_or_dash(&p.weak))
);
if let Some(why) = p.problem() {
anstream::println!(" {}", style::paint(style::DIM, format!("⚠ {why}")));
}
anstream::println!();
if let Ok(backend) = Backend::load(&session.kit, p) {
let d = backend.diagnostics();
anstream::println!("Plugin reports");
anstream::println!(
" {} {}",
style::label(15, "name"),
style::paint(style::PM, &d.name)
);
anstream::println!(" {} {}", style::label(15, "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)
);
anstream::println!();
}
}
let crate_name = session.kit.config().normalize_crate_name(name);
match session.kit.view(&crate_name) {
Ok(Some(info)) => {
found = true;
anstream::println!("crates.io");
anstream::println!(" {} {}", style::label(15, "crate"), info.name);
anstream::println!(
" {} {}",
style::label(15, "latest"),
style::paint(style::DIM, info.version)
);
if let Some(d) = info.description {
anstream::println!(
" {} {}",
style::label(15, "description"),
style::paint(style::DIM, d)
);
}
if let Some(r) = info.repository {
anstream::println!(
" {} {}",
style::label(15, "repository"),
style::paint(style::DIM, r)
);
}
}
Ok(None) => {
if !found {
anstream::println!("crates.io has no {crate_name}.");
}
}
Err(e) => {
anstream::eprintln!(
"{} {}",
style::paint(style::ERROR, "pmpx:"),
style::paint(style::ERROR_BODY, format!("crates.io lookup failed: {e}"))
);
}
}
if !found {
return Err(PmpxError::not_found(format!("no plugin named {name}.")));
}
Ok(EXIT_OK)
}
pub(super) fn join_or_dash(items: &[String]) -> String {
if items.is_empty() {
"(none)".to_string()
} else {
items.join(", ")
}
}
pub(super) fn describe_source(source: crate_plugin_kit::cache::InstallSource) -> &'static str {
match source {
crate_plugin_kit::cache::InstallSource::Prebuilt => "prebuilt",
crate_plugin_kit::cache::InstallSource::BuildHost => "built locally",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn join_or_dash_handles_the_empty_case() {
assert_eq!(join_or_dash(&[]), "(none)");
assert_eq!(join_or_dash(&["a".into(), "b".into()]), "a, b");
}
}