Skip to main content

zql_cli/db/layout/
flatten.rs

1use crate::db::dataset::{Column, Dataset};
2use crate::db::format::Format;
3use crate::error::MyResult;
4use crate::util::convert::{format_null, format_type};
5use crate::util::painter::{Painter, Style};
6use odbc_api::Nullability;
7use std::io::{Read, Write};
8use std::iter::once;
9
10const COLUMN_NAME: &str = "column";
11const TYPE_NAME: &str = "type";
12const NULL_NAME: &str = "nullable";
13const VALUE_NAME: &str = "value";
14
15#[derive(Clone, Copy)]
16pub enum FlattenVerbose {
17    Verbose,
18    Quiet,
19}
20
21pub struct FlattenLayout<'a, R: Read> {
22    dataset: Dataset<'a, R>,
23    columns: FlattenColumns,
24    dtypes: Option<FlattenTypes>,
25}
26
27impl<'a, R: Read> FlattenLayout<'a, R> {
28    pub fn from_dataset(dataset: Dataset<'a, R>, verbose: FlattenVerbose) -> Self {
29        let records = dataset.has_records();
30        let columns = FlattenColumns::from_columns(&dataset.columns, records);
31        let dtypes = match verbose {
32            FlattenVerbose::Verbose => Some(FlattenTypes::from_columns(&dataset.columns, records)),
33            FlattenVerbose::Quiet => None,
34        };
35        Self { dataset, columns, dtypes }
36    }
37
38    pub fn print_dataset<W: Write>(
39        &self,
40        writer: &mut W,
41        painter: Painter,
42    ) -> MyResult<()> {
43        if self.dataset.has_records() {
44            self.print_header(writer, painter)?;
45            self.dataset.get_measured(4096, |record, _| {
46                self.print_separator(writer, painter)?;
47                for index in 0..record.len() {
48                    let (value, format) = &record[index];
49                    self.print_record(writer, painter, value, format, index)?;
50                }
51                Ok(())
52            })?;
53        } else {
54            self.print_header(writer, painter)?;
55            self.print_separator(writer, painter)?;
56        }
57        Ok(())
58    }
59
60    fn print_header<W: Write>(
61        &self,
62        writer: &mut W,
63        painter: Painter,
64    ) -> MyResult<()> {
65        self.columns.print_header(writer, painter)?;
66        if let Some(dtypes) = &self.dtypes {
67            dtypes.print_header(writer, painter)?;
68        }
69        painter.print_value(writer, Style::Column, VALUE_NAME, 0, 0)?;
70        writeln!(writer)?;
71        Ok(())
72    }
73
74    fn print_separator<W: Write>(
75        &self,
76        writer: &mut W,
77        painter: Painter,
78    ) -> MyResult<()> {
79        self.columns.print_separator(writer, painter)?;
80        if let Some(dtypes) = &self.dtypes {
81            dtypes.print_separator(writer, painter)?;
82        }
83        painter.print_hyphens(writer, Style::Column, VALUE_NAME.len(), false)?;
84        writeln!(writer)?;
85        Ok(())
86    }
87
88    fn print_record<W: Write>(
89        &self,
90        writer: &mut W,
91        painter: Painter,
92        value: &str,
93        format: &Format,
94        index: usize,
95    ) -> MyResult<()> {
96        self.columns.print_record(writer, painter, index)?;
97        if let Some(dtypes) = &self.dtypes {
98            dtypes.print_record(writer, painter, index)?;
99        }
100        let style = format.style();
101        if value.is_empty() {
102            format_value(writer, painter, Style::Column, "-", 0, false)?;
103        } else {
104            format_value(writer, painter, style, value, 0, false)?;
105        }
106        writeln!(writer)?;
107        Ok(())
108    }
109}
110
111struct FlattenColumns {
112    names: Vec<String>,
113    width: usize,
114}
115
116// noinspection DuplicatedCode
117impl FlattenColumns {
118    fn from_columns(columns: &Vec<Column>, records: bool) -> Self {
119        if records {
120            let width = columns
121                .iter()
122                .map(|(_, width, _)| width.total())
123                .chain(once(COLUMN_NAME.len()))
124                .max()
125                .unwrap_or_default();
126            let names = columns
127                .iter()
128                .map(|(name, _, _)| name.to_string())
129                .collect();
130            Self { names, width }
131        } else {
132            let names = Vec::new();
133            let width = COLUMN_NAME.len();
134            Self { names, width }
135        }
136    }
137
138    fn print_header<W: Write>(
139        &self,
140        writer: &mut W,
141        painter: Painter,
142    ) -> MyResult<()> {
143        format_value(writer, painter, Style::Column, COLUMN_NAME, self.width, true)?;
144        Ok(())
145    }
146
147    fn print_separator<W: Write>(
148        &self,
149        writer: &mut W,
150        painter: Painter,
151    ) -> MyResult<()> {
152        painter.print_hyphens(writer, Style::Column, self.width, true)?;
153        Ok(())
154    }
155
156    fn print_record<W: Write>(
157        &self,
158        writer: &mut W,
159        painter: Painter,
160        index: usize,
161    ) -> MyResult<()> {
162        let name = self.names.get(index).map(String::as_ref).unwrap_or_default();
163        format_value(writer, painter, Style::Column, name, self.width, true)?;
164        Ok(())
165    }
166}
167
168struct FlattenTypes {
169    dtypes: Vec<String>,
170    nulls: Vec<Nullability>,
171    width: usize,
172}
173
174// noinspection DuplicatedCode
175impl FlattenTypes {
176    fn from_columns(columns: &Vec<Column>, records: bool) -> Self {
177        if records {
178            let dtypes = columns
179                .iter()
180                .map(|(_, _, metadata)| format_type(&metadata.dtype))
181                .collect::<Vec<_>>();
182            let nulls = columns
183                .iter()
184                .map(|(_, _, metadata)| metadata.null)
185                .collect();
186            let width = dtypes
187                .iter()
188                .map(|name| name.len())
189                .chain(once(TYPE_NAME.len()))
190                .max()
191                .unwrap_or_default();
192            Self { dtypes, nulls, width }
193        } else {
194            let dtypes = Vec::new();
195            let nulls = Vec::new();
196            let width = TYPE_NAME.len();
197            Self { dtypes, nulls, width }
198        }
199    }
200
201    fn print_header<W: Write>(
202        &self,
203        writer: &mut W,
204        painter: Painter,
205    ) -> MyResult<()> {
206        format_value(writer, painter, Style::Column, TYPE_NAME, self.width, true)?;
207        format_value(writer, painter, Style::Column, NULL_NAME, NULL_NAME.len(), true)?;
208        Ok(())
209    }
210
211    fn print_separator<W: Write>(
212        &self,
213        writer: &mut W,
214        painter: Painter,
215    ) -> MyResult<()> {
216        painter.print_hyphens(writer, Style::Column, self.width, true)?;
217        painter.print_hyphens(writer, Style::Column, NULL_NAME.len(), true)?;
218        Ok(())
219    }
220
221    fn print_record<W: Write>(
222        &self,
223        writer: &mut W,
224        painter: Painter,
225        index: usize,
226    ) -> MyResult<()> {
227        let dtype = self.dtypes.get(index).map(String::as_ref).unwrap_or_default();
228        let null = self.nulls.get(index).map(format_null).unwrap_or_default();
229        format_value(writer, painter, Style::Type, dtype, self.width, true)?;
230        format_value(writer, painter, Style::Type, null, NULL_NAME.len(), true)?;
231        Ok(())
232    }
233}
234
235fn format_value<W: Write>(
236    writer: &mut W,
237    painter: Painter,
238    style: Style,
239    value: &str,
240    width: usize,
241    inner: bool,
242) -> MyResult<()> {
243    if inner {
244        let right = (width + 2).checked_sub(value.len()).unwrap_or_default();
245        painter.print_value(writer, style, value, 0, right)?;
246    } else {
247        painter.print_value(writer, style, value, 0, 0)?;
248    }
249    Ok(())
250}