libcli_rs/output/
console.rs

1use std::any::Any;
2use std::io::Write;
3
4use anyhow::Result;
5use inflector::Inflector;
6use prettytable::{format, Cell, Row, Table};
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use crate::output::OutputTrait;
11
12pub struct ConsoleOutput;
13
14impl ConsoleOutput {
15    pub fn new() -> Self {
16        Self {}
17    }
18
19    fn display_obj(
20        &self,
21        table: &mut Table,
22        obj: &Value,
23        include_keys: &Option<Vec<&str>>,
24        exclude_keys: &Option<Vec<&str>>,
25    ) -> Result<()> {
26        let obj = obj.as_object().unwrap();
27
28        let predict_include_keys = |it: &(&String, &Value)| {
29            if let Some(keys) = include_keys {
30                keys.contains(&it.0.to_lowercase().as_str())
31            } else {
32                true
33            }
34        };
35
36        let predict_exclude_keys = |it: &(&String, &Value)| {
37            if let Some(keys) = exclude_keys {
38                !keys.contains(&it.0.to_lowercase().as_str())
39            } else {
40                true
41            }
42        };
43
44        if table.is_empty() {
45            let columns = obj
46                .iter()
47                .filter(predict_include_keys)
48                .filter(predict_exclude_keys)
49                .map(|it| Cell::new(&it.0.to_title_case()))
50                .collect();
51
52            table.add_row(Row::new(columns));
53        }
54
55        let column_values = obj
56            .iter()
57            .filter(predict_include_keys)
58            .filter(predict_exclude_keys)
59            .map(|it| Cell::new(&to_string_trim(it.1)))
60            .collect();
61
62        table.add_row(Row::new(column_values));
63
64        Ok(())
65    }
66}
67
68impl OutputTrait for ConsoleOutput {
69    fn display<'a, T: Deserialize<'a> + Serialize>(
70        &self,
71        writer: impl Write,
72        obj: &T,
73        include_keys: Option<Vec<&str>>,
74        exclude_keys: Option<Vec<&str>>,
75    ) -> Result<()> {
76        let mut writer = writer;
77        let obj = serde_json::to_value(obj)?;
78
79        let mut table = Table::new();
80        table.set_format(*format::consts::FORMAT_CLEAN);
81
82        match obj {
83            _ if obj.is_array() => {
84                for o in obj.as_array().unwrap() {
85                    self.display_obj(&mut table, o, &include_keys, &exclude_keys)?;
86                }
87            }
88
89            _ if obj.is_object() => {
90                self.display_obj(&mut table, &obj, &include_keys, &exclude_keys)?;
91            }
92
93            _ => Err(anyhow!("Unsupported display type: {:?}", obj.type_id()))?,
94        };
95
96        table
97            .print(&mut writer)
98            .map(|_it| ())
99            .map_err(|it| anyhow!(it))
100    }
101}
102
103fn to_string_trim(v: &Value) -> String {
104    match v {
105        Value::Null => "".to_string(),
106        Value::String(s) => truncate_str(s),
107        Value::Bool(b) => b.to_string(),
108        _ => serde_yaml::to_string(v)
109            .unwrap()
110            .trim_start_matches("---\n")
111            .to_string(),
112    }
113}
114
115fn truncate_str(str: &String) -> String {
116    if str.len() > 100 {
117        format!("{}...", &str[0..100])
118    } else {
119        str.clone()
120    }
121}