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