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
116impl 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
174impl 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}