use crate::{
BdatResult, BdatVersion, Cell, ColumnDef, ColumnMap, Label, Row, RowRef, RowRefMut, ValueType,
};
use thiserror::Error;
use util::VersionedIter;
pub mod cell;
pub mod column;
pub mod row;
mod legacy;
mod modern;
mod util;
pub use legacy::LegacyTable;
pub use modern::ModernTable;
#[derive(Debug, Clone, PartialEq)]
pub struct Table<'b> {
inner: TableInner<'b>,
}
#[derive(Debug, Clone, PartialEq)]
enum TableInner<'b> {
Modern(ModernTable<'b>),
Legacy(LegacyTable<'b>),
}
pub struct TableBuilder<'b> {
name: Label,
columns: ColumnMap,
rows: Vec<Row<'b>>,
}
#[derive(Error, Debug)]
pub enum FormatConvertError {
#[error("unsupported value type {0:?}")]
UnsupportedValueType(ValueType),
#[error("unsupported cell")]
UnsupportedCell,
}
pub trait TableAccessor<'t, 'b: 't> {
type Cell;
fn name(&self) -> &Label;
fn set_name(&mut self, name: Label);
fn base_id(&self) -> usize;
fn row(&'t self, id: usize) -> RowRef<'t, 'b, Self::Cell> {
self.get_row(id).expect("row not found")
}
fn row_mut(&'t mut self, id: usize) -> RowRefMut<'t, 'b> {
self.get_row_mut(id).expect("row not found")
}
fn get_row(&'t self, id: usize) -> Option<RowRef<'t, 'b, Self::Cell>>;
fn get_row_mut(&'t mut self, id: usize) -> Option<RowRefMut<'t, 'b>>;
fn row_count(&self) -> usize;
fn column_count(&self) -> usize;
}
macro_rules! versioned {
($var:expr, $name:ident) => {
match $var {
TableInner::Modern(m) => &m.$name,
TableInner::Legacy(l) => &l.$name,
}
};
($var:expr, $name:ident($($par:expr ) *)) => {
match $var {
TableInner::Modern(m) => m . $name ( $($par, )* ),
TableInner::Legacy(l) => l . $name ( $($par, )* ),
}
};
}
macro_rules! versioned_iter {
($var:expr, $name:ident($($par:expr ) *)) => {
match $var {
TableInner::Modern(m) => util::VersionedIter::Modern(m . $name ( $($par, )* )),
TableInner::Legacy(l) => util::VersionedIter::Legacy(l . $name ( $($par, )* )),
}
};
}
impl<'b> Table<'b> {
pub fn as_modern(&self) -> &ModernTable<'b> {
match &self.inner {
TableInner::Modern(m) => m,
_ => panic!("not modern"),
}
}
pub fn as_legacy(&self) -> &LegacyTable<'b> {
match &self.inner {
TableInner::Legacy(l) => l,
_ => panic!("not legacy"),
}
}
pub fn as_modern_mut(&mut self) -> &mut ModernTable<'b> {
match &mut self.inner {
TableInner::Modern(m) => m,
_ => panic!("not modern"),
}
}
pub fn as_legacy_mut(&mut self) -> &mut LegacyTable<'b> {
match &mut self.inner {
TableInner::Legacy(l) => l,
_ => panic!("not legacy"),
}
}
pub fn into_modern(self) -> ModernTable<'b> {
match self.inner {
TableInner::Modern(m) => m,
_ => panic!("not modern"),
}
}
pub fn into_legacy(self) -> LegacyTable<'b> {
match self.inner {
TableInner::Legacy(l) => l,
_ => panic!("not legacy"),
}
}
pub fn is_modern(&self) -> bool {
matches!(self.inner, TableInner::Modern(_))
}
pub fn is_legacy(&self) -> bool {
matches!(self.inner, TableInner::Legacy(_))
}
pub fn to_modern(self) -> BdatResult<ModernTable<'b>> {
match self.inner {
TableInner::Modern(m) => Ok(m),
TableInner::Legacy(l) => Ok(l.try_into()?),
}
}
pub fn to_legacy(self) -> BdatResult<LegacyTable<'b>> {
match self.inner {
TableInner::Modern(m) => Ok(m.try_into()?),
TableInner::Legacy(l) => Ok(l),
}
}
pub fn rows(&self) -> impl Iterator<Item = RowRef<'_, 'b>> {
match &self.inner {
TableInner::Modern(m) => VersionedIter::Modern(m.rows().map(RowRef::up_cast)),
TableInner::Legacy(l) => VersionedIter::Legacy(l.rows()),
}
}
pub fn rows_mut(&mut self) -> impl Iterator<Item = RowRefMut<'_, 'b>> {
versioned_iter!(&mut self.inner, rows_mut())
}
pub fn into_rows(self) -> impl Iterator<Item = Row<'b>> {
versioned_iter!(self.inner, into_rows())
}
pub fn columns(&self) -> impl Iterator<Item = &ColumnDef> {
versioned_iter!(&self.inner, columns())
}
pub fn columns_mut(&mut self) -> impl Iterator<Item = &mut ColumnDef> {
versioned_iter!(&mut self.inner, columns_mut())
}
pub fn into_columns(self) -> impl Iterator<Item = ColumnDef> {
versioned_iter!(self.inner, into_columns())
}
}
impl<'t, 'b: 't> TableAccessor<'t, 'b> for Table<'b> {
type Cell = &'t Cell<'b>;
fn name(&self) -> &Label {
versioned!(&self.inner, name)
}
fn set_name(&mut self, name: Label) {
versioned!(&mut self.inner, set_name(name))
}
fn base_id(&self) -> usize {
*versioned!(&self.inner, base_id)
}
fn row(&self, id: usize) -> RowRef<'_, 'b> {
match &self.inner {
TableInner::Modern(m) => m.row(id).up_cast(),
TableInner::Legacy(l) => l.row(id),
}
}
fn row_mut(&mut self, id: usize) -> RowRefMut<'_, 'b> {
versioned!(&mut self.inner, row_mut(id))
}
fn get_row(&self, id: usize) -> Option<RowRef<'_, 'b>> {
match &self.inner {
TableInner::Modern(m) => m.get_row(id).map(RowRef::up_cast),
TableInner::Legacy(l) => l.get_row(id),
}
}
fn get_row_mut(&mut self, id: usize) -> Option<RowRefMut<'_, 'b>> {
versioned!(&mut self.inner, get_row_mut(id))
}
fn row_count(&self) -> usize {
versioned!(&self.inner, row_count())
}
fn column_count(&self) -> usize {
versioned!(&self.inner, column_count())
}
}
impl<'b> TableBuilder<'b> {
pub fn with_name(name: Label) -> Self {
Self {
name,
columns: ColumnMap::default(),
rows: vec![],
}
}
pub fn add_column(mut self, column: ColumnDef) -> Self {
self.columns.push(column);
self
}
pub fn add_row(mut self, row: Row<'b>) -> Self {
self.rows.push(row);
self
}
pub fn set_rows(mut self, rows: Vec<Row<'b>>) -> Self {
self.rows = rows;
self
}
pub fn set_columns(mut self, columns: Vec<ColumnDef>) -> Self {
self.columns = ColumnMap::from(columns);
self
}
pub fn build_modern(self) -> ModernTable<'b> {
ModernTable::new(self)
}
pub fn build_legacy(self) -> LegacyTable<'b> {
LegacyTable::new(self)
}
pub fn build(self, version: BdatVersion) -> Table<'b> {
if version.is_legacy() {
self.build_legacy().into()
} else {
self.build_modern().into()
}
}
}