use anyhow::Result;
use cleanlib_client::{config, transport};
use crate::cache::{default_cache_dir, PersistentCache};
use crate::render::terminal;
use crate::render::json;
use crate::render::sarif;
use super::verdict_exit_code;
const CLI_VERSION: &str = env!("CARGO_PKG_VERSION");
pub async fn run(ecosystem: String, package: String, version: String, output: String) -> Result<()> {
let path = config::default_path();
let cfg = config::load_with_env_overrides(path.as_deref())?;
let client = transport::Client::from_config(&cfg)?;
let (verdict, from_cache) = match client.fetch_verdict(&ecosystem, &package, &version).await {
Ok(v) => {
if let Some(dir) = default_cache_dir() {
if let Ok(cache) = PersistentCache::open(&dir) {
let _ = cache.put(&ecosystem, &package, &version, v.clone());
}
}
(v, false)
}
Err(e) => {
if let Some(dir) = default_cache_dir() {
if let Ok(cache) = PersistentCache::open(&dir) {
if let Ok(Some(entry)) = cache.get(&ecosystem, &package, &version) {
if let Some(cached_at) = entry.cached_at() {
eprintln!(
"# warning: cleanlib-enrich unreachable; serving cached verdict (cached: {})",
cached_at.format("%Y-%m-%d")
);
} else {
eprintln!(
"# warning: cleanlib-enrich unreachable; serving cached verdict"
);
}
render_verdict(&entry.envelope, &output, &ecosystem, &package, &version)?;
let code = verdict_exit_code(&entry.envelope);
if code != 0 {
std::process::exit(code);
}
return Ok(());
}
}
}
return Err(e.into());
}
};
let _ = from_cache;
render_verdict(&verdict, &output, &ecosystem, &package, &version)?;
let code = verdict_exit_code(&verdict);
if code != 0 {
std::process::exit(code);
}
Ok(())
}
fn render_verdict(
v: &cleanlib_client::types::Verdict,
output: &str,
ecosystem: &str,
package: &str,
version: &str,
) -> Result<()> {
match output {
"json" => {
let state = cleanlib_client::CustomerState::from_wire(&v.source);
let mut value = serde_json::to_value(v)?;
if let serde_json::Value::Object(map) = &mut value {
map.insert("state".into(), state.as_str().into());
map.insert("state_label".into(), state.label().into());
map.insert(
"state_tier".into(),
match state.tier() {
cleanlib_client::Tier::Block => "block",
cleanlib_client::Tier::Warn => "warn",
cleanlib_client::Tier::Clean => "clean",
}
.into(),
);
}
json::render_verdict(v, &json::RenderOpts::default());
}
"text" => terminal::render_verdict(v, &terminal::RenderOpts::default()),
"sarif" => {
sarif::print_verdict_sarif(v, ecosystem, package, version, CLI_VERSION)
.map_err(|e| anyhow::anyhow!("failed to serialise SARIF: {e}"))?;
}
other => {
eprintln!(
"error: unsupported output format '{}'. valid options are: json, text, sarif",
other
);
std::process::exit(2);
}
}
Ok(())
}