use crate::schema::{ColumnName, DataType};
use crate::{AsAny, DeltaResult, Error};
use tracing::debug;
use std::collections::HashMap;
pub trait EngineList {
fn len(&self, row_index: usize) -> usize;
fn get(&self, row_index: usize, list_index: usize) -> String;
fn materialize(&self, row_index: usize) -> Vec<String>;
}
pub struct ListItem<'a> {
list: &'a dyn EngineList,
row: usize,
}
impl<'a> ListItem<'a> {
pub fn new(list: &'a dyn EngineList, row: usize) -> ListItem<'a> {
ListItem { list, row }
}
pub fn len(&self) -> usize {
self.list.len(self.row)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, list_index: usize) -> String {
self.list.get(self.row, list_index)
}
pub fn materialize(&self) -> Vec<String> {
self.list.materialize(self.row)
}
}
pub trait EngineMap {
fn get<'a>(&'a self, row_index: usize, key: &str) -> Option<&'a str>;
fn materialize(&self, row_index: usize) -> HashMap<String, String>;
}
pub struct MapItem<'a> {
map: &'a dyn EngineMap,
row: usize,
}
impl<'a> MapItem<'a> {
pub fn new(map: &'a dyn EngineMap, row: usize) -> MapItem<'a> {
MapItem { map, row }
}
pub fn get(&self, key: &str) -> Option<&'a str> {
self.map.get(self.row, key)
}
pub fn materialize(&self) -> HashMap<String, String> {
self.map.materialize(self.row)
}
}
macro_rules! impl_default_get {
( $(($name: ident, $typ: ty)), * ) => {
$(
fn $name(&'a self, _row_index: usize, field_name: &str) -> DeltaResult<Option<$typ>> {
debug!("Asked for type {} on {field_name}, but using default error impl.", stringify!($typ));
Err(Error::UnexpectedColumnType(format!("{field_name} is not of type {}", stringify!($typ))).with_backtrace())
}
)*
};
}
pub trait GetData<'a> {
impl_default_get!(
(get_bool, bool),
(get_int, i32),
(get_long, i64),
(get_str, &'a str),
(get_list, ListItem<'a>),
(get_map, MapItem<'a>)
);
}
macro_rules! impl_null_get {
( $(($name: ident, $typ: ty)), * ) => {
$(
fn $name(&'a self, _row_index: usize, _field_name: &str) -> DeltaResult<Option<$typ>> {
Ok(None)
}
)*
};
}
impl<'a> GetData<'a> for () {
impl_null_get!(
(get_bool, bool),
(get_int, i32),
(get_long, i64),
(get_str, &'a str),
(get_list, ListItem<'a>),
(get_map, MapItem<'a>)
);
}
pub trait TypedGetData<'a, T> {
fn get_opt(&'a self, row_index: usize, field_name: &str) -> DeltaResult<Option<T>>;
fn get(&'a self, row_index: usize, field_name: &str) -> DeltaResult<T> {
let val = self.get_opt(row_index, field_name)?;
val.ok_or_else(|| Error::MissingData(format!("Data missing for field {field_name}")))
}
}
macro_rules! impl_typed_get_data {
( $(($name: ident, $typ: ty)), * ) => {
$(
impl<'a> TypedGetData<'a, $typ> for dyn GetData<'a> +'_ {
fn get_opt(&'a self, row_index: usize, field_name: &str) -> DeltaResult<Option<$typ>> {
self.$name(row_index, field_name)
}
}
)*
};
}
impl_typed_get_data!(
(get_bool, bool),
(get_int, i32),
(get_long, i64),
(get_str, &'a str),
(get_list, ListItem<'a>),
(get_map, MapItem<'a>)
);
impl<'a> TypedGetData<'a, String> for dyn GetData<'a> + '_ {
fn get_opt(&'a self, row_index: usize, field_name: &str) -> DeltaResult<Option<String>> {
self.get_str(row_index, field_name)
.map(|s| s.map(|s| s.to_string()))
}
}
impl<'a> TypedGetData<'a, Vec<String>> for dyn GetData<'a> + '_ {
fn get_opt(&'a self, row_index: usize, field_name: &str) -> DeltaResult<Option<Vec<String>>> {
let list_opt: Option<ListItem<'_>> = self.get_opt(row_index, field_name)?;
Ok(list_opt.map(|list| list.materialize()))
}
}
impl<'a> TypedGetData<'a, HashMap<String, String>> for dyn GetData<'a> + '_ {
fn get_opt(
&'a self,
row_index: usize,
field_name: &str,
) -> DeltaResult<Option<HashMap<String, String>>> {
let map_opt: Option<MapItem<'_>> = self.get_opt(row_index, field_name)?;
Ok(map_opt.map(|map| map.materialize()))
}
}
pub trait RowVisitor {
fn selected_column_names_and_types(&self) -> (&'static [ColumnName], &'static [DataType]);
fn visit<'a>(&mut self, row_count: usize, getters: &[&'a dyn GetData<'a>]) -> DeltaResult<()>;
fn visit_rows_of(&mut self, data: &dyn EngineData) -> DeltaResult<()>
where
Self: Sized,
{
data.visit_rows(self.selected_column_names_and_types().0, self)
}
}
pub trait EngineData: AsAny {
fn visit_rows(
&self,
column_names: &[ColumnName],
visitor: &mut dyn RowVisitor,
) -> DeltaResult<()>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}