1use anyhow::{Context, Result};
2use comfy_table::Table;
3use jmespath::compile;
4use serde::Serialize;
5use serde_json::Value;
6
7#[derive(Debug, Clone, Copy, clap::ValueEnum)]
8pub enum OutputFormat {
9 Json,
10 Yaml,
11 Table,
12}
13
14impl Default for OutputFormat {
15 fn default() -> Self {
16 Self::Json
17 }
18}
19
20pub fn print_output<T: Serialize>(
21 data: T,
22 format: OutputFormat,
23 query: Option<&str>,
24) -> Result<()> {
25 let mut json_value = serde_json::to_value(data)?;
26
27 if let Some(query_str) = query {
29 let expr = compile(query_str).context("Invalid JMESPath expression")?;
30 let json_str = serde_json::to_string(&json_value)?;
32 let data = jmespath::Variable::from_json(&json_str)
33 .map_err(|e| anyhow::anyhow!("Failed to parse JSON for JMESPath: {}", e))?;
34 let result = expr.search(&data).context("JMESPath query failed")?;
35 let result_str = result.to_string();
37 json_value =
38 serde_json::from_str(&result_str).context("Failed to parse JMESPath result")?;
39 }
40
41 match format {
42 OutputFormat::Json => {
43 println!("{}", serde_json::to_string_pretty(&json_value)?);
44 }
45 OutputFormat::Yaml => {
46 println!("{}", serde_yaml::to_string(&json_value)?);
47 }
48 OutputFormat::Table => {
49 print_as_table(&json_value)?;
50 }
51 }
52
53 Ok(())
54}
55
56fn print_as_table(value: &Value) -> Result<()> {
57 match value {
58 Value::Array(arr) if !arr.is_empty() => {
59 let mut table = Table::new();
60
61 if let Value::Object(first) = &arr[0] {
63 let headers: Vec<String> = first.keys().cloned().collect();
64 table.set_header(&headers);
65
66 for item in arr {
68 if let Value::Object(obj) = item {
69 let row: Vec<String> = headers
70 .iter()
71 .map(|h| format_value(obj.get(h).unwrap_or(&Value::Null)))
72 .collect();
73 table.add_row(row);
74 }
75 }
76 } else {
77 table.set_header(vec!["Value"]);
79 for item in arr {
80 table.add_row(vec![format_value(item)]);
81 }
82 }
83
84 println!("{}", table);
85 }
86 Value::Object(obj) => {
87 let mut table = Table::new();
88 table.set_header(vec!["Key", "Value"]);
89
90 for (key, val) in obj {
91 table.add_row(vec![key.clone(), format_value(val)]);
92 }
93
94 println!("{}", table);
95 }
96 _ => {
97 println!("{}", format_value(value));
98 }
99 }
100
101 Ok(())
102}
103
104fn format_value(value: &Value) -> String {
105 match value {
106 Value::Null => "null".to_string(),
107 Value::Bool(b) => b.to_string(),
108 Value::Number(n) => n.to_string(),
109 Value::String(s) => s.clone(),
110 Value::Array(arr) => format!("[{} items]", arr.len()),
111 Value::Object(obj) => format!("{{{} fields}}", obj.len()),
112 }
113}