indodax_cli/output/
table.rs1use super::CommandOutput;
2use comfy_table::presets::UTF8_FULL;
3use comfy_table::{Cell, Color, ContentArrangement, Table};
4
5pub fn render(output: &CommandOutput) -> String {
6 if output.headers.is_empty() && output.rows.is_empty() {
7 if let Some(ref addendum) = output.addendum {
8 return addendum.clone();
9 }
10 return serde_json::to_string_pretty(&output.data).unwrap_or_default();
11 }
12
13 let mut table = Table::new();
14 table
15 .load_preset(UTF8_FULL)
16 .set_content_arrangement(ContentArrangement::Dynamic)
17 .set_header(
18 output
19 .headers
20 .iter()
21 .map(|h| Cell::new(h).fg(Color::Cyan))
22 .collect::<Vec<_>>(),
23 );
24
25 for row in &output.rows {
26 table.add_row(row.iter().map(Cell::new).collect::<Vec<_>>());
27 }
28
29 let mut result = table.to_string();
30
31 if let Some(ref addendum) = output.addendum {
32 result.push('\n');
33 result.push_str(addendum);
34 }
35
36 result
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use serde_json::json;
43
44 #[test]
45 fn test_render_with_headers_and_rows() {
46 let output = CommandOutput {
47 data: json!({}),
48 headers: vec!["Col1".into(), "Col2".into()],
49 rows: vec![
50 vec!["a".into(), "b".into()],
51 vec!["c".into(), "d".into()],
52 ],
53 format: super::super::OutputFormat::Table,
54 addendum: None,
55 warnings: vec![],
56 suppress_final_output: false,
57 };
58
59 let rendered = render(&output);
60 assert!(rendered.contains("Col1"));
61 assert!(rendered.contains("Col2"));
62 assert!(rendered.contains("a"));
63 assert!(rendered.contains("b"));
64 }
65
66 #[test]
67 fn test_render_empty_headers_and_rows() {
68 let output = CommandOutput {
69 data: json!({"fallback": "data"}),
70 headers: vec![],
71 rows: vec![],
72 format: super::super::OutputFormat::Table,
73 addendum: None,
74 warnings: vec![],
75 suppress_final_output: false,
76 };
77
78 let rendered = render(&output);
79 assert!(rendered.contains("fallback") || rendered.contains("data") || rendered.contains("{}"));
81 }
82
83 #[test]
84 fn test_render_with_addendum() {
85 let output = CommandOutput {
86 data: json!({}),
87 headers: vec!["H".into()],
88 rows: vec![vec!["v".into()]],
89 format: super::super::OutputFormat::Table,
90 addendum: Some("Extra info here".into()),
91 warnings: vec![],
92 suppress_final_output: false,
93 };
94
95 let rendered = render(&output);
96 assert!(rendered.contains("Extra info here"));
97 }
98
99 #[test]
100 fn test_render_single_row() {
101 let output = CommandOutput {
102 data: json!({}),
103 headers: vec!["Name".into()],
104 rows: vec![vec!["Alice".into()]],
105 format: super::super::OutputFormat::Table,
106 addendum: None,
107 warnings: vec![],
108 suppress_final_output: false,
109 };
110
111 let rendered = render(&output);
112 assert!(rendered.contains("Name"));
113 assert!(rendered.contains("Alice"));
114 }
115
116 #[test]
117 fn test_render_multiple_rows() {
118 let output = CommandOutput {
119 data: json!({}),
120 headers: vec!["ID".into(), "Value".into()],
121 rows: vec![
122 vec!["1".into(), "100".into()],
123 vec!["2".into(), "200".into()],
124 vec!["3".into(), "300".into()],
125 ],
126 format: super::super::OutputFormat::Table,
127 addendum: None,
128 warnings: vec![],
129 suppress_final_output: false,
130 };
131
132 let rendered = render(&output);
133 assert!(rendered.contains("ID"));
134 assert!(rendered.contains("Value"));
135 assert!(rendered.contains("100"));
136 assert!(rendered.contains("200"));
137 assert!(rendered.contains("300"));
138 }
139
140 #[test]
141 fn test_render_no_addendum() {
142 let output = CommandOutput {
143 data: json!({}),
144 headers: vec!["H".into()],
145 rows: vec![vec!["v".into()]],
146 format: super::super::OutputFormat::Table,
147 addendum: None,
148 warnings: vec![],
149 suppress_final_output: false,
150 };
151
152 let rendered = render(&output);
153 assert!(!rendered.ends_with('\n') || !rendered.trim().is_empty());
155 }
156
157 #[test]
158 fn test_render_empty_data_with_addendum() {
159 let output = CommandOutput {
160 data: json!({}),
161 headers: vec![],
162 rows: vec![],
163 format: super::super::OutputFormat::Table,
164 addendum: Some("Only addendum".into()),
165 warnings: vec![],
166 suppress_final_output: false,
167 };
168
169 let rendered = render(&output);
170 assert!(rendered.contains("Only addendum"));
171 }
172}