Skip to main content

indodax_cli/output/
table.rs

1use 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        // Should fall back to JSON rendering of data
80        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        // Should not contain extra newline at end if no addendum
154        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}