1use crate::protocol::Envelope;
2use anyhow::Result;
3use serde::Serialize;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum OutputFormat {
9 Json,
10 Yaml,
11 Text,
12 Ndjson,
13}
14
15impl FromStr for OutputFormat {
16 type Err = anyhow::Error;
17
18 fn from_str(s: &str) -> Result<Self> {
19 match s.to_lowercase().as_str() {
20 "json" | "json-schema" => Ok(OutputFormat::Json),
21 "yaml" => Ok(OutputFormat::Yaml),
22 "text" => Ok(OutputFormat::Text),
23 "ndjson" => Ok(OutputFormat::Ndjson),
24 _ => Err(anyhow::anyhow!("Invalid output format: {}", s)),
25 }
26 }
27}
28
29pub fn print_envelope<T: Serialize>(envelope: &Envelope<T>, format: OutputFormat) -> Result<()> {
31 match format {
32 OutputFormat::Json => {
33 let json = serde_json::to_string_pretty(envelope)?;
34 println!("{}", json);
35 }
36 OutputFormat::Yaml => {
37 let yaml = serde_yaml::to_string(envelope)?;
38 println!("{}", yaml);
39 }
40 OutputFormat::Text => {
41 print_envelope_text(envelope)?;
42 }
43 OutputFormat::Ndjson => {
44 let json = serde_json::to_string(envelope)?;
46 println!("{}", json);
47 }
48 }
49 Ok(())
50}
51
52pub fn print_ndjson<T: Serialize>(items: &[T]) -> Result<()> {
54 for item in items {
55 let json = serde_json::to_string(item)?;
56 println!("{}", json);
57 }
58 Ok(())
59}
60
61fn print_envelope_text<T: Serialize>(envelope: &Envelope<T>) -> Result<()> {
63 if envelope.ok {
64 if let Some(ref data) = envelope.data {
65 let json = serde_json::to_value(data)?;
66 print_value_text(&json, 0);
67 } else {
68 println!("Success");
69 }
70 } else if let Some(ref error) = envelope.error {
71 eprintln!("Error: {:?} - {}", error.code, error.message);
72 if let Some(ref details) = error.details {
73 eprintln!("Details:");
74 let json = serde_json::to_value(details)?;
75 print_value_text(&json, 1);
76 }
77 }
78 Ok(())
79}
80
81fn print_value_text(value: &serde_json::Value, indent: usize) {
83 let prefix = " ".repeat(indent);
84 match value {
85 serde_json::Value::Object(map) => {
86 for (key, val) in map {
87 match val {
88 serde_json::Value::Object(_) | serde_json::Value::Array(_) => {
89 println!("{}{}:", prefix, key);
90 print_value_text(val, indent + 1);
91 }
92 _ => {
93 println!("{}{}: {}", prefix, key, format_value(val));
94 }
95 }
96 }
97 }
98 serde_json::Value::Array(arr) => {
99 for (i, val) in arr.iter().enumerate() {
100 match val {
101 serde_json::Value::Object(_) | serde_json::Value::Array(_) => {
102 println!("{}[{}]:", prefix, i);
103 print_value_text(val, indent + 1);
104 }
105 _ => {
106 println!("{}[{}] {}", prefix, i, format_value(val));
107 }
108 }
109 }
110 }
111 _ => {
112 println!("{}{}", prefix, format_value(value));
113 }
114 }
115}
116
117fn format_value(value: &serde_json::Value) -> String {
118 match value {
119 serde_json::Value::String(s) => s.clone(),
120 serde_json::Value::Number(n) => n.to_string(),
121 serde_json::Value::Bool(b) => b.to_string(),
122 serde_json::Value::Null => "null".to_string(),
123 _ => value.to_string(),
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::protocol::{ErrorCode, ErrorDetails};
131 use std::str::FromStr;
132
133 #[test]
134 fn test_output_format_from_str() {
135 assert_eq!(OutputFormat::from_str("json").unwrap(), OutputFormat::Json);
136 assert_eq!(OutputFormat::from_str("JSON").unwrap(), OutputFormat::Json);
137 assert_eq!(OutputFormat::from_str("yaml").unwrap(), OutputFormat::Yaml);
138 assert_eq!(OutputFormat::from_str("text").unwrap(), OutputFormat::Text);
139 assert!(OutputFormat::from_str("invalid").is_err());
140 }
141
142 #[test]
143 fn test_print_json_envelope() {
144 let envelope = Envelope::success("test", "data");
145 let result = print_envelope(&envelope, OutputFormat::Json);
146 assert!(result.is_ok());
147 }
148
149 #[test]
150 fn test_print_error_envelope() {
151 let error = ErrorDetails::new(ErrorCode::InvalidArgument, "test error");
152 let envelope = Envelope::<()>::error("test", error);
153 let result = print_envelope(&envelope, OutputFormat::Json);
154 assert!(result.is_ok());
155 }
156}