proof_of_sql/base/database/
table.rsuse super::{Column, ColumnField};
use crate::base::{map::IndexMap, scalar::Scalar};
use alloc::vec::Vec;
use proof_of_sql_parser::Identifier;
use snafu::Snafu;
#[derive(Debug, Default, Clone, Copy)]
pub struct TableOptions {
pub row_count: Option<usize>,
}
impl TableOptions {
#[must_use]
pub fn new(row_count: Option<usize>) -> Self {
Self { row_count }
}
}
#[derive(Snafu, Debug, PartialEq, Eq)]
pub enum TableError {
#[snafu(display("Columns have different lengths"))]
ColumnLengthMismatch,
#[snafu(display("Column has length different from the provided row count"))]
ColumnLengthMismatchWithSpecifiedRowCount,
#[snafu(display("Table is empty and no row count is specified"))]
EmptyTableWithoutSpecifiedRowCount,
}
#[derive(Debug, Clone, Eq)]
pub struct Table<'a, S: Scalar> {
table: IndexMap<Identifier, Column<'a, S>>,
row_count: usize,
}
impl<'a, S: Scalar> Table<'a, S> {
pub fn try_new(table: IndexMap<Identifier, Column<'a, S>>) -> Result<Self, TableError> {
Self::try_new_with_options(table, TableOptions::default())
}
pub fn try_new_with_options(
table: IndexMap<Identifier, Column<'a, S>>,
options: TableOptions,
) -> Result<Self, TableError> {
match (table.is_empty(), options.row_count) {
(true, None) => Err(TableError::EmptyTableWithoutSpecifiedRowCount),
(true, Some(row_count)) => Ok(Self { table, row_count }),
(false, None) => {
let row_count = table[0].len();
if table.values().any(|column| column.len() != row_count) {
Err(TableError::ColumnLengthMismatch)
} else {
Ok(Self { table, row_count })
}
}
(false, Some(row_count)) => {
if table.values().any(|column| column.len() != row_count) {
Err(TableError::ColumnLengthMismatchWithSpecifiedRowCount)
} else {
Ok(Self { table, row_count })
}
}
}
}
pub fn try_from_iter<T: IntoIterator<Item = (Identifier, Column<'a, S>)>>(
iter: T,
) -> Result<Self, TableError> {
Self::try_from_iter_with_options(iter, TableOptions::default())
}
pub fn try_from_iter_with_options<T: IntoIterator<Item = (Identifier, Column<'a, S>)>>(
iter: T,
options: TableOptions,
) -> Result<Self, TableError> {
Self::try_new_with_options(IndexMap::from_iter(iter), options)
}
#[must_use]
pub fn num_columns(&self) -> usize {
self.table.len()
}
#[must_use]
pub fn num_rows(&self) -> usize {
self.row_count
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
#[must_use]
pub fn into_inner(self) -> IndexMap<Identifier, Column<'a, S>> {
self.table
}
#[must_use]
pub fn inner_table(&self) -> &IndexMap<Identifier, Column<'a, S>> {
&self.table
}
#[must_use]
pub fn schema(&self) -> Vec<ColumnField> {
self.table
.iter()
.map(|(name, column)| ColumnField::new(*name, column.column_type()))
.collect()
}
pub fn column_names(&self) -> impl Iterator<Item = &Identifier> {
self.table.keys()
}
pub fn columns(&self) -> impl Iterator<Item = &Column<'a, S>> {
self.table.values()
}
#[must_use]
pub fn column(&self, index: usize) -> Option<&Column<'a, S>> {
self.table.values().nth(index)
}
}
impl<S: Scalar> PartialEq for Table<'_, S> {
fn eq(&self, other: &Self) -> bool {
self.table == other.table
&& self
.table
.keys()
.zip(other.table.keys())
.all(|(a, b)| a == b)
}
}
#[cfg(test)]
impl<'a, S: Scalar> core::ops::Index<&str> for Table<'a, S> {
type Output = Column<'a, S>;
fn index(&self, index: &str) -> &Self::Output {
self.table
.get(&index.parse::<Identifier>().unwrap())
.unwrap()
}
}