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 if result.ends_with('\n') {
31 result.pop();
32 }
33
34 if let Some(ref addendum) = output.addendum {
35 result.push('\n');
36 result.push_str(addendum);
37 }
38
39 result
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use serde_json::json;
46
47 #[test]
48 fn test_render_with_headers_and_rows() {
49 let output = CommandOutput {
50 data: json!({}),
51 headers: vec!["Col1".into(), "Col2".into()],
52 rows: vec![
53 vec!["a".into(), "b".into()],
54 vec!["c".into(), "d".into()],
55 ],
56 format: super::super::OutputFormat::Table,
57 addendum: None,
58 };
59
60 let rendered = render(&output);
61 assert!(rendered.contains("Col1"));
62 assert!(rendered.contains("Col2"));
63 assert!(rendered.contains("a"));
64 assert!(rendered.contains("b"));
65 }
66
67 #[test]
68 fn test_render_empty_headers_and_rows() {
69 let output = CommandOutput {
70 data: json!({"fallback": "data"}),
71 headers: vec![],
72 rows: vec![],
73 format: super::super::OutputFormat::Table,
74 addendum: None,
75 };
76
77 let rendered = render(&output);
78 assert!(rendered.contains("fallback") || rendered.contains("data") || rendered.contains("{}"));
80 }
81
82 #[test]
83 fn test_render_with_addendum() {
84 let output = CommandOutput {
85 data: json!({}),
86 headers: vec!["H".into()],
87 rows: vec![vec!["v".into()]],
88 format: super::super::OutputFormat::Table,
89 addendum: Some("Extra info here".into()),
90 };
91
92 let rendered = render(&output);
93 assert!(rendered.contains("Extra info here"));
94 }
95
96 #[test]
97 fn test_render_single_row() {
98 let output = CommandOutput {
99 data: json!({}),
100 headers: vec!["Name".into()],
101 rows: vec![vec!["Alice".into()]],
102 format: super::super::OutputFormat::Table,
103 addendum: None,
104 };
105
106 let rendered = render(&output);
107 assert!(rendered.contains("Name"));
108 assert!(rendered.contains("Alice"));
109 }
110
111 #[test]
112 fn test_render_multiple_rows() {
113 let output = CommandOutput {
114 data: json!({}),
115 headers: vec!["ID".into(), "Value".into()],
116 rows: vec![
117 vec!["1".into(), "100".into()],
118 vec!["2".into(), "200".into()],
119 vec!["3".into(), "300".into()],
120 ],
121 format: super::super::OutputFormat::Table,
122 addendum: None,
123 };
124
125 let rendered = render(&output);
126 assert!(rendered.contains("ID"));
127 assert!(rendered.contains("Value"));
128 assert!(rendered.contains("100"));
129 assert!(rendered.contains("200"));
130 assert!(rendered.contains("300"));
131 }
132
133 #[test]
134 fn test_render_no_addendum() {
135 let output = CommandOutput {
136 data: json!({}),
137 headers: vec!["H".into()],
138 rows: vec![vec!["v".into()]],
139 format: super::super::OutputFormat::Table,
140 addendum: None,
141 };
142
143 let rendered = render(&output);
144 assert!(!rendered.ends_with('\n') || rendered.trim().len() > 0);
146 }
147
148 #[test]
149 fn test_render_empty_data_with_addendum() {
150 let output = CommandOutput {
151 data: json!({}),
152 headers: vec![],
153 rows: vec![],
154 format: super::super::OutputFormat::Table,
155 addendum: Some("Only addendum".into()),
156 };
157
158 let rendered = render(&output);
159 assert!(rendered.contains("Only addendum"));
160 }
161}