use std::io::Write;
use arrow_array::RecordBatch;
use arrow_schema::SchemaRef;
use comfy_table::{Table, presets};
use crate::Result;
use crate::cli::BinaryFormat;
use crate::output::{RowWriter, value};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TableStyle {
Pretty,
Plain,
}
pub struct TableRowWriter<W: Write> {
writer: W,
binary_format: BinaryFormat,
table_style: TableStyle,
schema: Option<SchemaRef>,
rows: Vec<Vec<String>>,
}
impl<W: Write> TableRowWriter<W> {
pub fn new(writer: W, binary_format: BinaryFormat, table_style: TableStyle) -> Self {
Self {
writer,
binary_format,
table_style,
schema: None,
rows: Vec::new(),
}
}
}
impl<W: Write> RowWriter for TableRowWriter<W> {
fn start(&mut self, schema: &SchemaRef) -> Result<()> {
self.schema = Some(schema.clone());
Ok(())
}
fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> {
let rows = (0..batch.num_rows())
.map(|row| {
batch
.columns()
.iter()
.map(|column| value::table_cell(column.as_ref(), row, self.binary_format))
.collect::<Result<Vec<_>>>()
})
.collect::<Result<Vec<_>>>()?;
self.rows.extend(rows);
Ok(())
}
fn finish(&mut self) -> Result<()> {
let schema = self
.schema
.as_ref()
.expect("start() must be called before finish()");
let mut table = Table::new();
let preset = match self.table_style {
TableStyle::Plain => presets::ASCII_FULL,
TableStyle::Pretty => presets::UTF8_FULL,
};
table.load_preset(preset);
let headers: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
table.set_header(headers);
table.add_rows(self.rows.drain(..));
writeln!(self.writer, "{table}")?;
self.writer.flush()?;
Ok(())
}
}