use crate::internal::category::Category;
use crate::internal::column::Column;
use crate::internal::streamname;
use crate::internal::stringpool::StringPool;
use crate::internal::value::{Value, ValueRef};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Index;
use std::rc::Rc;
#[derive(Clone)]
pub struct Table {
name: String,
columns: Vec<Column>,
long_string_refs: bool,
}
impl Table {
pub(crate) fn new(
name: String,
columns: Vec<Column>,
long_string_refs: bool,
) -> Rc<Table> {
Rc::new(Table { name, columns, long_string_refs })
}
pub fn name(&self) -> &str {
&self.name
}
pub(crate) fn stream_name(&self) -> String {
streamname::encode(&self.name, true)
}
pub(crate) fn is_valid_name(name: &str) -> bool {
Category::Identifier.validate(name) && streamname::is_valid(name, true)
}
pub(crate) fn long_string_refs(&self) -> bool {
self.long_string_refs
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn has_column(&self, column_name: &str) -> bool {
self.index_for_column_name(column_name).is_some()
}
pub fn get_column(&self, column_name: &str) -> Option<&Column> {
match self.index_for_column_name(column_name) {
Some(index) => Some(&self.columns[index]),
None => None,
}
}
pub fn primary_key_indices(&self) -> Vec<usize> {
self.columns
.iter()
.enumerate()
.filter_map(|(index, column)| {
if column.is_primary_key() {
Some(index)
} else {
None
}
})
.collect()
}
pub(crate) fn index_for_column_name(
&self,
column_name: &str,
) -> Option<usize> {
for (index, column) in self.columns.iter().enumerate() {
if column.name() == column_name {
return Some(index);
}
}
None
}
pub(crate) fn read_rows<R: Read + Seek>(
&self,
mut reader: R,
) -> io::Result<Vec<Vec<ValueRef>>> {
let data_length = reader.seek(SeekFrom::End(0))?;
reader.rewind()?;
let row_size = self
.columns
.iter()
.map(|col| col.coltype().width(self.long_string_refs))
.sum::<u64>();
let num_columns = self.columns.len();
let num_rows =
if row_size > 0 { (data_length / row_size) as usize } else { 0 };
let mut rows =
vec![Vec::<ValueRef>::with_capacity(num_columns); num_rows];
for column in self.columns.iter() {
let coltype = column.coltype();
for row in rows.iter_mut() {
row.push(
coltype.read_value(&mut reader, self.long_string_refs)?,
);
}
}
Ok(rows)
}
pub(crate) fn write_rows<W: Write>(
&self,
mut writer: W,
rows: Vec<Vec<ValueRef>>,
) -> io::Result<()> {
for (index, column) in self.columns.iter().enumerate() {
let coltype = column.coltype();
for row in rows.iter() {
coltype.write_value(
&mut writer,
row[index],
self.long_string_refs,
)?;
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct Row {
table: Rc<Table>,
values: Vec<Value>,
}
impl Row {
pub(crate) fn new(table: Rc<Table>, values: Vec<Value>) -> Row {
debug_assert_eq!(values.len(), table.columns().len());
Row { table, values }
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn columns(&self) -> &[Column] {
self.table.columns()
}
pub fn has_column(&self, column_name: &str) -> bool {
self.table.has_column(column_name)
}
}
impl Index<usize> for Row {
type Output = Value;
fn index(&self, index: usize) -> &Value {
debug_assert_eq!(self.values.len(), self.table.columns().len());
if index < self.values.len() {
&self.values[index]
} else if self.table.name.is_empty() {
panic!(
"Anonymous table has only {} columns (index was {index})",
self.values.len()
);
} else {
panic!(
"Table {:?} has only {} columns (index was {index})",
self.table.name,
self.values.len()
);
}
}
}
impl<'a> Index<&'a str> for Row {
type Output = Value;
fn index(&self, column_name: &str) -> &Value {
match self.table.index_for_column_name(column_name) {
Some(index) => &self.values[index],
None => {
if self.table.name.is_empty() {
panic!(
"Anonymous table has no column named {column_name:?}"
);
} else {
panic!(
"Table {:?} has no column named {column_name:?}",
self.table.name
);
}
}
}
}
}
pub struct Rows<'a> {
string_pool: &'a StringPool,
table: Rc<Table>,
rows: Vec<Vec<ValueRef>>,
next_row_index: usize,
}
impl<'a> Rows<'a> {
pub(crate) fn new(
string_pool: &'a StringPool,
table: Rc<Table>,
rows: Vec<Vec<ValueRef>>,
) -> Rows<'a> {
Rows { table, string_pool, rows, next_row_index: 0 }
}
pub fn columns(&self) -> &[Column] {
self.table.columns()
}
pub(crate) fn into_table_and_values(
self,
) -> (Rc<Table>, Vec<Vec<ValueRef>>) {
(self.table, self.rows)
}
}
impl<'a> Iterator for Rows<'a> {
type Item = Row;
fn next(&mut self) -> Option<Row> {
if self.next_row_index < self.rows.len() {
let values: Vec<Value> = self.rows[self.next_row_index]
.iter()
.map(|value_ref| value_ref.to_value(self.string_pool))
.collect();
self.next_row_index += 1;
Some(Row::new(self.table.clone(), values))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
debug_assert!(self.next_row_index <= self.rows.len());
let remaining_rows = self.rows.len() - self.next_row_index;
(remaining_rows, Some(remaining_rows))
}
}
impl<'a> ExactSizeIterator for Rows<'a> {}
#[cfg(test)]
mod tests {
use super::Table;
#[test]
fn valid_table_name() {
assert!(Table::is_valid_name("fooBar"));
assert!(Table::is_valid_name("_Validation"));
assert!(Table::is_valid_name("Catch22"));
assert!(Table::is_valid_name("Foo.Bar"));
assert!(!Table::is_valid_name(""));
assert!(!Table::is_valid_name("99Bottles"));
assert!(!Table::is_valid_name(
"ThisStringIsWayTooLongToBeATableNameIMeanSeriouslyWhoWouldTryTo\
UseANameThatIsThisLongItWouldBePrettySilly"
));
}
}