1use serde::Serialize;
2use serde_json::Value;
3use std::io::{self, IsTerminal, Write};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum OutputFormat {
7 Json,
8 Table,
9}
10
11impl OutputFormat {
12 pub fn resolve(explicit: Option<&str>) -> Self {
13 match explicit {
14 Some("json") => OutputFormat::Json,
15 Some("table") => OutputFormat::Table,
16 Some(_) => OutputFormat::Table,
17 None => {
18 if io::stdout().is_terminal() {
19 OutputFormat::Table
20 } else {
21 OutputFormat::Json
22 }
23 }
24 }
25 }
26}
27
28#[derive(Debug, Serialize)]
29pub struct CliError {
30 pub error: String,
31 pub message: String,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub suggestion: Option<String>,
34 pub retryable: bool,
35}
36
37impl CliError {
38 pub fn print(&self, format: OutputFormat) {
39 let stderr = io::stderr();
40 let mut out = stderr.lock();
41 match format {
42 OutputFormat::Json => {
43 let _ = serde_json::to_writer(&mut out, self);
44 let _ = writeln!(out);
45 }
46 OutputFormat::Table => {
47 let _ = writeln!(out, "error: {}", self.message);
48 if let Some(ref suggestion) = self.suggestion {
49 let _ = writeln!(out, "suggestion: {suggestion}");
50 }
51 }
52 }
53 }
54}
55
56pub fn print_json(value: &Value) {
57 let stdout = io::stdout();
58 let mut out = stdout.lock();
59 let _ = serde_json::to_writer_pretty(&mut out, value);
60 let _ = writeln!(out);
61}
62
63pub fn print_value(value: &Value, format: OutputFormat) {
64 match format {
65 OutputFormat::Json => print_json(value),
66 OutputFormat::Table => {
67 if let Some(obj) = value.as_object() {
68 for (k, v) in obj {
69 let display = match v {
70 Value::String(s) => s.clone(),
71 Value::Null => "n/a".to_string(),
72 other => other.to_string(),
73 };
74 println!(" {k}: {display}");
75 }
76 } else {
77 println!("{}", value);
78 }
79 }
80 }
81}