diff --git a/src/queries.rs b/src/queries.rs index a007780..d130b2c 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -1,5 +1,5 @@ use crate::api::ApiClient; -use crossterm::style::{Color, Stylize}; +use crossterm::style::Stylize; use serde::{Deserialize, Serialize}; const SQL_KEYWORDS: &[&str] = &[ @@ -125,6 +125,8 @@ struct QueryRun { sql_hash: String, sql_text: String, result_id: Option, + #[serde(default)] + database_id: Option, error_message: Option, warning_message: Option, trace_id: Option, @@ -139,16 +141,6 @@ struct ListResponse { next_cursor: Option, } -fn color_status(status: &str) -> String { - let color = match status { - "succeeded" => Color::Green, - "failed" => Color::Red, - "running" | "queued" | "pending" => Color::Yellow, - _ => Color::Reset, - }; - status.with(color).to_string() -} - fn truncate_sql(sql: &str, max: usize) -> String { let flat = sql.split_whitespace().collect::>().join(" "); if flat.chars().count() <= max { @@ -191,7 +183,7 @@ pub fn list( .map(|r| { vec![ r.id.clone(), - color_status(&r.status), + crate::util::color_status(&r.status), crate::util::format_date(&r.created_at), r.execution_time_ms .map(|ms| ms.to_string()) @@ -199,12 +191,13 @@ pub fn list( r.row_count .map(|n| n.to_string()) .unwrap_or_else(|| "-".to_string()), - truncate_sql(&r.sql_text, 60), + r.result_id.as_deref().unwrap_or("-").to_string(), + truncate_sql(&r.sql_text, 40), ] }) .collect(); crate::table::print( - &["ID", "STATUS", "CREATED", "DURATION_MS", "ROWS", "SQL"], + &["ID", "STATUS", "CREATED", "MS", "ROWS", "RESULT_ID", "SQL"], &rows, ); } @@ -238,7 +231,7 @@ fn print_detail(r: &QueryRun, format: &str) { "table" => { let label = |l: &str| format!("{:<14}", l).dark_grey().to_string(); println!("{}{}", label("id:"), r.id); - println!("{}{}", label("status:"), color_status(&r.status)); + println!("{}{}", label("status:"), crate::util::color_status(&r.status)); println!( "{}{}", label("created:"), @@ -259,6 +252,9 @@ fn print_detail(r: &QueryRun, format: &str) { if let Some(ref id) = r.result_id { println!("{}{}", label("result id:"), id); } + if let Some(ref id) = r.database_id { + println!("{}{}", label("database id:"), id); + } if let Some(ref id) = r.saved_query_id { let version = r .saved_query_version diff --git a/src/results.rs b/src/results.rs index d1ff56f..66f7688 100644 --- a/src/results.rs +++ b/src/results.rs @@ -1,4 +1,5 @@ use crate::api::ApiClient; +use crossterm::style::Stylize; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] @@ -6,6 +7,12 @@ struct ResultEntry { id: String, status: String, created_at: String, + #[serde(default)] + row_count: Option, + #[serde(default)] + query_run_id: Option, + #[serde(default)] + expires_at: Option, } #[derive(Deserialize)] @@ -29,25 +36,48 @@ pub fn list(workspace_id: &str, limit: Option, offset: Option, format: "yaml" => print!("{}", serde_yaml::to_string(&body.results).unwrap()), "table" => { if body.results.is_empty() { - use crossterm::style::Stylize; eprintln!("{}", "No results found.".dark_grey()); } else { + let has_rows = body.results.iter().any(|r| r.row_count.is_some()); + let has_query_run = body.results.iter().any(|r| r.query_run_id.is_some()); + let has_expires = body.results.iter().any(|r| r.expires_at.is_some()); + let rows: Vec> = body .results .iter() .map(|r| { - vec![ + let mut row = vec![ r.id.clone(), - r.status.clone(), + crate::util::color_status(&r.status), crate::util::format_date(&r.created_at), - ] + ]; + if has_rows { + row.push(r.row_count.map(|n| n.to_string()).unwrap_or_else(|| "-".to_string())); + } + if has_query_run { + row.push(r.query_run_id.as_deref().unwrap_or("-").to_string()); + } + if has_expires { + row.push( + r.expires_at + .as_deref() + .map(|s| crate::util::format_date(s)) + .unwrap_or_else(|| "-".to_string()), + ); + } + row }) .collect(); - crate::table::print(&["ID", "STATUS", "CREATED AT"], &rows); + + let mut headers = vec!["ID", "STATUS", "CREATED"]; + if has_rows { headers.push("ROWS"); } + if has_query_run { headers.push("QUERY_RUN_ID"); } + if has_expires { headers.push("EXPIRES"); } + + crate::table::print(&headers, &rows); } if body.has_more { let next = offset.unwrap_or(0) + body.count as u32; - use crossterm::style::Stylize; eprintln!( "{}", format!( diff --git a/src/util.rs b/src/util.rs index 8bf3b8b..0f422eb 100644 --- a/src/util.rs +++ b/src/util.rs @@ -328,6 +328,20 @@ fn colorize_json_value(v: &str) -> String { format!("{colored}{comma}") } +/// Color a status string for terminal output. Covers vocabulary from both +/// query runs (succeeded/failed/running/queued/pending) and results (ready/expired/processing). +pub fn color_status(status: &str) -> String { + use crossterm::style::{Color, Stylize}; + let color = match status { + "succeeded" | "ready" => Color::Green, + "failed" => Color::Red, + "running" | "queued" | "pending" | "processing" => Color::Yellow, + "expired" => Color::DarkGrey, + _ => Color::Reset, + }; + status.with(color).to_string() +} + /// Format an ISO date string compactly: "2024-03-15 14:23" (no seconds, no timezone). pub fn format_date(s: &str) -> String { let s = s.split('.').next().unwrap_or(s).trim_end_matches('Z');