cleanlib-cli 0.1.0

Terminal interface to CleanLibrary — query dependency verdicts and scan package manifests for ALLOW / DENY / WARN signals from the terminal or CI pipelines.
//! `cleanlib audit` (cycle-7 Cli2). Migrates `cmd_audit` from `main.rs`.

use anyhow::Result;
use cleanlib_client::{config, transport};
use comfy_table::{presets, ContentArrangement, Table};

use crate::render::terminal::{stdout_is_tty, style_decision};

pub async fn run(
    since: Option<String>,
    decision: Option<String>,
    ecosystem: Option<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 resp = client
        .audit(since.as_deref(), decision.as_deref(), ecosystem.as_deref())
        .await?;

    match output.as_str() {
        "json" => println!("{}", serde_json::to_string_pretty(&resp)?),
        _ => {
            if resp.entries.is_empty() {
                println!("(no audit entries match the given filters)");
            } else {
                let mut table = Table::new();
                let preset = if stdout_is_tty() {
                    presets::UTF8_BORDERS_ONLY
                } else {
                    presets::NOTHING
                };
                table
                    .load_preset(preset)
                    .set_content_arrangement(ContentArrangement::Dynamic)
                    .set_header(vec![
                        "AT", "REQUEST_ID", "ECOSYSTEM", "PACKAGE", "VERSION", "DECISION", "REASON",
                    ]);
                for e in &resp.entries {
                    table.add_row(vec![
                        e.at.clone(),
                        e.request_id.clone(),
                        e.ecosystem.clone(),
                        e.package.clone(),
                        e.version.clone(),
                        style_decision(&e.decision),
                        e.reason.clone(),
                    ]);
                }
                println!("{table}");
            }
            if let Some(cursor) = &resp.next_cursor {
                eprintln!("# more results available; next_cursor={}", cursor);
            }
        }
    }
    Ok(())
}