use std::path::{Path, PathBuf};
use anyhow::Result;
use cleanlib_client::{config, transport, types};
use crate::render::terminal;
use super::scan::parse_packages_file;
use super::scan_exit_code;
pub async fn preview(
policy_path: PathBuf,
packages_path: PathBuf,
ecosystem: 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 packages = parse_packages_file(&packages_path, &ecosystem)?;
let policy = parse_policy_file(&policy_path)?;
let req = types::PolicyPreviewRequest {
packages,
policy: Some(policy),
};
let resp = client.policy_preview(&req).await?;
match output.as_str() {
"json" => println!("{}", serde_json::to_string_pretty(&resp)?),
_ => terminal::render_decisions(&resp.decisions),
}
let code = scan_exit_code(&resp.decisions);
if code != 0 {
std::process::exit(code);
}
Ok(())
}
pub fn parse_policy_file(path: &Path) -> Result<serde_json::Value> {
let content = std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("read {}: {}", path.display(), e))?;
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&content) {
return Ok(v);
}
let toml_val: toml::Value = toml::from_str(&content)
.map_err(|e| anyhow::anyhow!("{}: not valid JSON; TOML parse: {}", path.display(), e))?;
let json_val = serde_json::to_value(toml_val)
.map_err(|e| anyhow::anyhow!("{}: TOML→JSON conversion: {}", path.display(), e))?;
Ok(json_val)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_json_policy() {
let dir = std::env::temp_dir();
let p = dir.join("cleanlib-policy-json-test.json");
std::fs::write(&p, r#"{"rules": [{"id": 1}]}"#).unwrap();
let v = parse_policy_file(&p).unwrap();
assert_eq!(v["rules"][0]["id"], 1);
let _ = std::fs::remove_file(&p);
}
#[test]
fn parse_toml_policy() {
let dir = std::env::temp_dir();
let p = dir.join("cleanlib-policy-toml-test.toml");
std::fs::write(&p, "[meta]\nname = \"strict\"\n").unwrap();
let v = parse_policy_file(&p).unwrap();
assert_eq!(v["meta"]["name"], "strict");
let _ = std::fs::remove_file(&p);
}
}