use std::{fmt::Debug, iter::once};
use crate::{Error, ReadableVec, Result, VecIndex, VecValue, Version};
use super::{LazyColumnSumVec, LazyColumnVec};
const FNV_OFFSET: u32 = 0x811c_9dc5;
const FNV_PRIME: u32 = 0x0100_0193;
pub trait ColumnId: Copy + Debug + Eq + Ord + Send + Sync + 'static {
type Row<T>: VecValue
where
T: VecValue;
const VERSION: Version;
const ALL: &'static [Self];
fn index(self) -> usize;
fn get<T: VecValue>(self, row: &Self::Row<T>) -> &T;
fn get_mut<T: VecValue>(self, row: &mut Self::Row<T>) -> &mut T;
fn from_fn<T, F>(f: F) -> Self::Row<T>
where
T: VecValue,
F: FnMut(Self) -> T;
fn map<T, U, F>(row: Self::Row<T>, f: F) -> Self::Row<U>
where
T: VecValue,
U: VecValue,
F: FnMut(T) -> U;
fn map_ref<T, U, F>(row: &Self::Row<T>, mut f: F) -> Self::Row<U>
where
T: VecValue,
U: VecValue,
F: FnMut(&T) -> U,
{
Self::from_fn(|column| f(column.get(row)))
}
}
pub trait ReadableColumnarVec<C>: ReadableVec<Self::I, C::Row<Self::T>> + Clone
where
C: ColumnId,
{
type I: VecIndex;
type T: VecValue;
fn for_each_column_chunk_at<F>(&self, columns: &[C], from: usize, to: usize, f: &mut F)
where
F: FnMut(C, usize, &[Self::T]);
fn column(&self, name: &str, version: Version, column: C) -> LazyColumnVec<Self, C>
where
Self: Sized,
{
LazyColumnVec::new(name, version, self.clone(), column)
}
fn sum_columns(
&self,
name: &str,
version: Version,
columns: impl IntoIterator<Item = C>,
) -> LazyColumnSumVec<Self, C>
where
Self: Sized,
{
LazyColumnSumVec::new(name, version, self.clone(), columns)
}
}
pub(super) fn validate_schema<C: ColumnId>() -> Result<Version> {
if C::ALL.is_empty() {
return Err(Error::InvalidArgument(
"ColumnarVec requires at least one column",
));
}
let mut fingerprint = FNV_OFFSET;
let mut names = Vec::with_capacity(C::ALL.len());
for (index, &column) in C::ALL.iter().enumerate() {
if column.index() != index {
return Err(Error::InvalidArgument(
"ColumnId::ALL must contain every column in physical index order",
));
}
let name = format!("{column:?}");
if name.is_empty() {
return Err(Error::InvalidArgument(
"ColumnId column names cannot be empty",
));
}
if names.contains(&name) {
return Err(Error::InvalidArgument(
"ColumnId column names must be unique",
));
}
for byte in name.bytes().chain(once(0)) {
fingerprint ^= u32::from(byte);
fingerprint = fingerprint.wrapping_mul(FNV_PRIME);
}
names.push(name);
}
Ok(Version::new(fingerprint))
}
pub(super) fn validate_column<C: ColumnId>(column: C) {
let index = column.index();
assert_eq!(
C::ALL.get(index),
Some(&column),
"invalid column ID at physical index {index}",
);
}