use std::collections::{HashMap, HashSet};
use bytes::Bytes;
use delta_kernel_derive::internal_api;
use crate::error::add_scalar_path_context;
use crate::expressions::{Scalar, StructData};
use crate::schema::{ArrayType, DataType, MapType, StructField, StructType, ToSchema};
use crate::utils::require;
use crate::{DeltaResult, Error};
#[internal_api]
pub(crate) trait ToDataType {
fn to_data_type() -> DataType;
}
impl<T: ToSchema> ToDataType for T {
fn to_data_type() -> DataType {
T::to_schema().into()
}
}
macro_rules! impl_to_data_type {
( $(($rust_type: ty, $data_type: expr)), * ) => {
$(
impl ToDataType for $rust_type {
fn to_data_type() -> DataType {
$data_type
}
}
)*
};
}
impl_to_data_type!(
(String, DataType::STRING),
(Bytes, DataType::BINARY),
(i64, DataType::LONG),
(i32, DataType::INTEGER),
(i16, DataType::SHORT),
(i8, DataType::BYTE),
(f32, DataType::FLOAT),
(f64, DataType::DOUBLE),
(bool, DataType::BOOLEAN)
);
impl<T: ToDataType> ToDataType for Vec<T> {
fn to_data_type() -> DataType {
ArrayType::new(T::to_data_type(), false).into()
}
}
impl<T: ToDataType> ToDataType for Vec<Option<T>> {
fn to_data_type() -> DataType {
ArrayType::new(T::to_data_type(), true).into()
}
}
impl<T: ToDataType> ToDataType for HashSet<T> {
fn to_data_type() -> DataType {
ArrayType::new(T::to_data_type(), false).into()
}
}
impl<K: ToDataType, V: ToDataType> ToDataType for HashMap<K, V> {
fn to_data_type() -> DataType {
MapType::new(K::to_data_type(), V::to_data_type(), false).into()
}
}
impl<K: ToDataType, V: ToDataType> ToDataType for HashMap<K, Option<V>> {
fn to_data_type() -> DataType {
MapType::new(K::to_data_type(), V::to_data_type(), true).into()
}
}
#[internal_api]
pub(crate) trait GetStructField {
fn get_struct_field(name: impl Into<String>) -> StructField;
}
impl<T: ToDataType> GetStructField for T {
fn get_struct_field(name: impl Into<String>) -> StructField {
StructField::not_null(name, T::to_data_type())
}
}
impl<T: ToDataType> GetStructField for Option<T> {
fn get_struct_field(name: impl Into<String>) -> StructField {
StructField::nullable(name, T::to_data_type())
}
}
pub(crate) trait ToNullableContainerType {
fn to_nullable_container_type() -> DataType;
}
impl<K: ToDataType, V: ToDataType> ToNullableContainerType for HashMap<K, V> {
fn to_nullable_container_type() -> DataType {
MapType::new(K::to_data_type(), V::to_data_type(), true).into()
}
}
#[internal_api]
pub(crate) trait GetNullableContainerStructField {
fn get_nullable_container_struct_field(name: impl Into<String>) -> StructField;
}
impl<T: ToNullableContainerType> GetNullableContainerStructField for T {
fn get_nullable_container_struct_field(name: impl Into<String>) -> StructField {
StructField::not_null(name, T::to_nullable_container_type())
}
}
impl<T: ToNullableContainerType> GetNullableContainerStructField for Option<T> {
fn get_nullable_container_struct_field(name: impl Into<String>) -> StructField {
StructField::nullable(name, T::to_nullable_container_type())
}
}
#[internal_api]
pub(crate) struct StructDataFields {
expected: StructType,
fields: HashMap<String, (StructField, Scalar)>,
}
impl StructDataFields {
pub(crate) fn try_new(data: StructData, expected: StructType) -> DeltaResult<Self> {
let (actual_fields, values) = data.into_parts();
require!(
actual_fields.len() == values.len(),
Error::scalar_conversion(
format!("{} struct values", actual_fields.len()),
format!("{} struct values", values.len()),
)
);
require!(
actual_fields.len() == expected.num_fields(),
Error::scalar_conversion(
format!("struct with {} fields", expected.num_fields()),
format!("struct with {} fields", actual_fields.len()),
)
);
let mut fields = HashMap::with_capacity(actual_fields.len());
for (field, value) in actual_fields.into_iter().zip(values) {
let name = field.name().clone();
match fields.entry(name) {
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert((field, value));
}
std::collections::hash_map::Entry::Occupied(entry) => {
return Err(add_scalar_path_context(
Error::scalar_conversion("one field", "duplicate fields"),
entry.key().clone(),
));
}
}
}
Ok(Self { expected, fields })
}
pub(crate) fn take_field<T: TryFrom<Scalar, Error = Error>>(
&mut self,
field_name: &str,
) -> DeltaResult<T> {
let expected = self.expected.field(field_name).ok_or_else(|| {
Error::InternalError(format!(
"Derived schema does not contain generated field {field_name:?}"
))
})?;
let (actual_field, value) = self.fields.remove(field_name).ok_or_else(|| {
add_scalar_path_context(
Error::scalar_conversion("present field", "missing field"),
field_name,
)
})?;
require!(
actual_field.is_nullable() == expected.is_nullable(),
add_scalar_path_context(
Error::scalar_conversion(
if expected.is_nullable() {
"nullable field"
} else {
"non-nullable field"
},
if actual_field.is_nullable() {
"nullable field"
} else {
"non-nullable field"
},
),
field_name,
)
);
T::try_from(value).map_err(|error| add_scalar_path_context(error, field_name))
}
pub(crate) fn finish(self) -> DeltaResult<()> {
if self.fields.is_empty() {
return Ok(());
}
let mut extra: Vec<_> = self.fields.keys().collect();
extra.sort_unstable();
Err(Error::scalar_conversion(
"no additional fields",
format!("fields {extra:?}"),
))
}
}