//! `pic logs ` — print recent execution log rows. use anyhow::Result; use picloud_shared::ExecutionStatus; use crate::client::Client; use crate::config::load; pub async fn run(script_id: &str, limit: u32) -> Result<()> { let creds = load()?; let client = Client::from_creds(&creds)?; let entries = client.logs_list(script_id, limit).await?; for e in entries { let summary = summarize(&e.response_body, &e.script_logs); println!( "{}\t{}\t{}", e.created_at.to_rfc3339(), status_label(&e.status), truncate(&summary, 120), ); } Ok(()) } fn status_label(s: &ExecutionStatus) -> &'static str { match s { ExecutionStatus::Success => "success", ExecutionStatus::Error => "error", ExecutionStatus::Timeout => "timeout", ExecutionStatus::BudgetExceeded => "budget_exceeded", } } fn summarize(response_body: &Option, script_logs: &serde_json::Value) -> String { // Prefer the last script-side log line (often the most useful for // grepping). Fall back to the response body. if let Some(arr) = script_logs.as_array() { if let Some(last) = arr.last() { if let Some(msg) = last.get("message").and_then(|m| m.as_str()) { return msg.to_string(); } } } response_body .as_ref() .map(ToString::to_string) .unwrap_or_else(|| "-".to_string()) } fn truncate(s: &str, n: usize) -> String { let normalized = s.replace('\n', " "); if normalized.chars().count() <= n { normalized } else { let head: String = normalized.chars().take(n).collect(); format!("{head}…") } }