use crate::{
error::Error,
eval::{Schema, State, Table},
span::{ResultExt, S},
value::Value,
};
use std::{convert::TryInto, mem};
pub trait Writer {
fn write_value(&mut self, value: &Value) -> Result<(), S<Error>>;
fn write_file_header(&mut self, schema: &Schema<'_>) -> Result<(), S<Error>>;
fn write_header(&mut self, schema: &Schema<'_>) -> Result<(), S<Error>>;
fn write_value_separator(&mut self) -> Result<(), S<Error>>;
fn write_row_separator(&mut self) -> Result<(), S<Error>>;
fn write_trailer(&mut self) -> Result<(), S<Error>>;
}
#[derive(Debug)]
struct TableState<'a, W: Writer> {
table: &'a Table,
schema: Schema<'a>,
writer: W,
fresh: bool,
empty: bool,
}
#[derive(Debug)]
pub struct Env<'a, W: Writer> {
state: &'a mut State,
qualified: bool,
tables: Vec<TableState<'a, W>>,
}
impl<'a, W: Writer> Env<'a, W> {
pub fn new(
tables: &'a [Table],
state: &'a mut State,
qualified: bool,
mut new_writer: impl FnMut(&Table) -> Result<W, S<Error>>,
) -> Result<Self, S<Error>> {
Ok(Self {
tables: tables
.iter()
.map(|table| {
let mut writer = new_writer(table)?;
let schema = table.schema(qualified);
writer.write_file_header(&schema)?;
Ok::<_, S<Error>>(TableState {
table,
schema,
writer,
fresh: true,
empty: true,
})
})
.collect::<Result<_, _>>()?,
state,
qualified,
})
}
pub fn tables(&mut self) -> impl Iterator<Item = (&'a Table, &mut W)> + '_ {
self.tables.iter_mut().map(|table| (table.table, &mut table.writer))
}
fn write_one_row(&mut self, table_index: usize) -> Result<(), S<Error>> {
let table = &mut self.tables[table_index];
if mem::take(&mut table.empty) {
table.writer.write_header(&table.schema)
} else {
table.writer.write_row_separator()
}?;
let values = table.table.row.eval(self.state)?;
for (col_index, value) in values.iter().enumerate() {
if col_index != 0 {
table.writer.write_value_separator()?;
}
table.writer.write_value(value)?;
}
for (child, count) in &table.table.derived {
let count = count.eval(self.state)?.try_into().span_err(count.0.span)?;
for r in 1..=count {
self.state.sub_row_num = r;
self.write_one_row(*child)?;
}
}
Ok(())
}
fn mark_descendant_visited(&mut self, root: usize) {
let mut ids = vec![root];
while let Some(id) = ids.pop() {
let table = &mut self.tables[id];
table.fresh = false;
ids.extend(table.table.derived.iter().map(|child| child.0));
}
}
pub fn write_row(&mut self) -> Result<(), S<Error>> {
for table in &mut self.tables {
table.fresh = true;
}
for i in 0..self.tables.len() {
if self.tables[i].fresh {
self.mark_descendant_visited(i);
self.state.sub_row_num = 1;
self.write_one_row(i)?;
}
}
self.state.increase_row_num();
Ok(())
}
pub fn write_trailer(&mut self) -> Result<(), S<Error>> {
for table in &mut self.tables {
if !mem::replace(&mut table.empty, true) {
table.writer.write_trailer()?;
}
}
Ok(())
}
}