use crate::catalog::{IndexKind, ValType};
use crate::spec::IndexSpec;
use crate::table_error::TableError;
use kevy_text::SortOrder;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct TableIndex {
pub column: Vec<u8>,
pub kind: IndexKind,
pub values: Vec<Vec<u8>>,
}
impl TableIndex {
pub fn new(column: impl Into<Vec<u8>>, kind: IndexKind) -> TableIndex {
TableIndex { column: column.into(), kind, values: Vec::new() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct OrderPath {
pub name: Vec<u8>,
pub on: Vec<(Vec<u8>, SortOrder)>,
}
impl OrderPath {
pub fn new(name: impl Into<Vec<u8>>, on: Vec<(Vec<u8>, SortOrder)>) -> OrderPath {
OrderPath { name: name.into(), on }
}
pub(crate) fn led_ascending_by(&self, column: &[u8]) -> bool {
self.on.first().is_some_and(|(c, o)| c == column && *o == SortOrder::Asc)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct WindowSpec {
pub column: Vec<u8>,
pub span: i64,
pub bucket: i64,
}
impl WindowSpec {
pub fn new(column: impl Into<Vec<u8>>, span: i64, bucket: i64) -> WindowSpec {
WindowSpec { column: column.into(), span, bucket }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub struct TableSpec {
pub name: Vec<u8>,
pub prefix: Vec<u8>,
pub pk: Vec<u8>,
pub columns: Vec<(Vec<u8>, ValType)>,
pub indexes: Vec<TableIndex>,
pub orderpaths: Vec<OrderPath>,
pub window: Option<WindowSpec>,
pub autodeclare: usize,
pub auto_added: Vec<Vec<u8>>,
}
pub const MAX_TABLES: usize = 64;
impl TableSpec {
pub fn compile(&self) -> Result<Vec<IndexSpec>, TableError> {
compile_table(self)
}
pub fn column_type(&self, col: &[u8]) -> Option<ValType> {
self.columns.iter().find(|(n, _)| n == col).map(|(_, t)| *t)
}
#[must_use]
pub fn sans_auto(&self) -> TableSpec {
let mut s = self.clone();
let auto = std::mem::take(&mut s.auto_added);
let suffix_of = |entry: &[u8]| -> Option<Vec<u8>> {
let e = entry.split(|&b| b == b'#').next()?;
let dot = e.iter().position(|&b| b == b'.')?;
Some(e[dot + 1..].to_vec())
};
for entry in &auto {
if let Some(pos) = entry.iter().position(|&b| b == b'#') {
let field = &entry[pos + 1..];
if let Some(sfx) = suffix_of(entry)
&& let Some(ix) = s.indexes.iter_mut().find(|ix| ix.column == sfx)
{
ix.values.retain(|v| v != field);
}
} else if let Some(sfx) = suffix_of(entry) {
s.indexes.retain(|ix| ix.column != sfx);
s.orderpaths.retain(|op| op.name != sfx);
}
}
s
}
pub fn validate(&self) -> Result<(), TableError> {
if self.name.is_empty() {
return Err(TableError::EmptyName);
}
if self.prefix.is_empty() {
return Err(TableError::EmptyPrefix);
}
if self.columns.is_empty() {
return Err(TableError::NoColumns);
}
self.validate_columns_and_pk()?;
self.validate_indexes()?;
self.validate_orderpaths()?;
self.validate_window()
}
}
pub use crate::table_catalog::TableCatalog;
pub(crate) fn dotted(table: &[u8], suffix: &[u8]) -> Vec<u8> {
let mut n = table.to_vec();
n.push(b'.');
n.extend_from_slice(suffix);
n
}
#[path = "table_compile.rs"]
mod compile;
use compile::compile_table;
#[cfg(test)]
#[path = "table_tests.rs"]
mod tests;