1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
mod table;
mod serde;
mod describe;

pub use output::Terminal;
pub use output::OutputType;

pub use table::TableOutputHandler;
use table::TableRenderer;

use self::serde::SerdeRenderer;
pub use self::serde::SerializeType;

use self::describe::DescribeObjectRender;
pub use self::describe::DescribeObjectHandler;

pub use context::RenderContext;

pub trait KeyValOutputHandler {
    fn key_values(&self) -> Vec<(String, Option<String>)>;
}

mod context {

    use std::sync::Arc;

    use async_trait::async_trait;
    use super::Terminal;

    #[async_trait]
    pub trait RenderContext {
        async fn render_on<O: Terminal>(&self, out: Arc<O>);
    }
}

pub use self::error::OutputError;

mod error {

    use serde_json::Error as SerdeJsonError;
    use serde_yaml::Error as SerdeYamlError;

    #[derive(thiserror::Error, Debug)]
    pub enum OutputError {
        #[error(transparent)]
        SerdeJson {
            #[from]
            source: SerdeJsonError,
        },
        #[error("Fluvio client error")]
        SerdeYamlError {
            #[from]
            source: SerdeYamlError,
        },
    }
}

#[allow(clippy::module_inception)]
mod output {

    use std::sync::Arc;

    use structopt::clap::arg_enum;
    use serde::Serialize;
    use prettytable::format;
    use prettytable::Table;
    use prettytable::row;
    use prettytable::cell;

    use super::TableOutputHandler;
    use super::TableRenderer;
    use super::SerdeRenderer;
    use super::DescribeObjectHandler;
    use super::DescribeObjectRender;
    use super::KeyValOutputHandler;
    use super::SerializeType;
    use super::OutputError;

    // Uses clap::arg_enum to choose possible variables
    arg_enum! {
        #[derive(Debug, Clone, PartialEq)]
        #[allow(non_camel_case_types)]
        pub enum OutputType {
            table,
            yaml,
            json,
        }
    }

    /// OutputType defaults to table formatting
    impl ::std::default::Default for OutputType {
        fn default() -> Self {
            OutputType::table
        }
    }

    /// OutputType check if table
    impl OutputType {
        pub fn is_table(&self) -> bool {
            *self == OutputType::table
        }
    }

    pub trait Terminal: Sized {
        fn print(&self, msg: &str);
        fn println(&self, msg: &str);

        fn render_list<T>(self: Arc<Self>, list: &T, mode: OutputType) -> Result<(), OutputError>
        where
            T: TableOutputHandler + Serialize,
        {
            if mode.is_table() {
                let render = TableRenderer::new(self);
                render.render(list, false);
            } else {
                let render = SerdeRenderer::new(self);
                render.render(&list, mode.into())?;
            }

            Ok(())
        }

        fn render_table<T: TableOutputHandler>(self: Arc<Self>, val: &T, indent: bool) {
            let render = TableRenderer::new(self);
            render.render(val, indent);
        }

        fn render_serde<T: Serialize>(
            self: Arc<Self>,
            val: &T,
            mode: SerializeType,
        ) -> Result<(), OutputError> {
            let render = SerdeRenderer::new(self);
            render.render(val, mode)
        }

        /// describe objects
        fn describe_objects<D>(
            self: Arc<Self>,
            objects: &[D],
            mode: OutputType,
        ) -> Result<(), OutputError>
        where
            D: DescribeObjectHandler + TableOutputHandler + KeyValOutputHandler + Serialize + Clone,
        {
            let render = DescribeObjectRender::new(self);
            render.render(objects, mode)
        }

        /// print something that can be rendered as key values
        fn render_key_values<K: KeyValOutputHandler>(&self, key_val: &K) {
            let kv_values = key_val.key_values();

            // Create the table
            let mut table = Table::new();
            table.set_format(*format::consts::FORMAT_CLEAN);

            for (key, val_opt) in kv_values {
                if let Some(val) = val_opt {
                    table.add_row(row!(key, ":".to_owned(), val));
                } else {
                    table.add_row(row!(key));
                }
            }

            // print table to stdout
            table.printstd();
        }
    }
}