use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_ensure;
use vortex_error::vortex_err;
use crate::ArrayRef;
use crate::ArraySlots;
use crate::IntoArray;
use crate::array::Array;
use crate::array::ArrayParts;
use crate::array::EmptyArrayData;
use crate::array::TypedArrayRef;
use crate::array_slots;
use crate::arrays::ConstantArray;
use crate::arrays::PrimitiveArray;
use crate::arrays::Union;
use crate::arrays::union::union_type_ids_dtype;
use crate::dtype::DType;
use crate::dtype::Nullability;
use crate::dtype::PType;
use crate::dtype::UnionVariants;
use crate::scalar::Scalar;
#[array_slots(Union)]
pub struct UnionSlots {
#[slot(0)]
pub type_ids: ArrayRef,
#[slot(1..)]
pub children: Vec<ArrayRef>,
}
pub(super) fn make_union_parts(
type_ids: ArrayRef,
variants: UnionVariants,
children: impl IntoIterator<Item = ArrayRef>,
) -> ArrayParts<Union> {
let len = type_ids.len();
let nullability = type_ids.dtype().nullability();
let children = children.into_iter();
let (lower, _) = children.size_hint();
let mut slots = ArraySlots::with_capacity(UnionSlots::CHILDREN_OFFSET + lower);
slots.push(Some(type_ids));
slots.extend(children.map(Some));
ArrayParts::new(
Union,
DType::Union(variants, nullability),
len,
EmptyArrayData,
)
.with_slots(slots)
}
pub struct UnionDataParts {
pub variants: UnionVariants,
pub type_ids: ArrayRef,
pub children: Vec<ArrayRef>,
}
pub trait UnionArrayExt: UnionArraySlotsExt {
fn variants(&self) -> &UnionVariants {
match self.as_ref().dtype() {
DType::Union(variants, _) => variants,
_ => unreachable!("UnionArrayExt requires a union dtype"),
}
}
fn iter_children(&self) -> impl ExactSizeIterator<Item = &ArrayRef> + '_ {
self.children().iter()
}
fn child(&self, index: usize) -> Option<&ArrayRef> {
self.children().get(index)
}
fn child_by_type_id(&self, type_id: u8) -> Option<&ArrayRef> {
self.child(self.variants().tag_to_child_index(type_id)?)
}
fn child_by_name_opt(&self, name: impl AsRef<str>) -> Option<&ArrayRef> {
self.child(self.variants().find(name)?)
}
fn child_by_name(&self, name: impl AsRef<str>) -> VortexResult<&ArrayRef> {
let name = name.as_ref();
self.child_by_name_opt(name).ok_or_else(|| {
vortex_err!(
"Variant {name} not found in union array with names {:?}",
self.variants().names()
)
})
}
}
impl<T: TypedArrayRef<Union>> UnionArrayExt for T {}
impl Array<Union> {
pub fn new(
type_ids: ArrayRef,
variants: UnionVariants,
children: impl IntoIterator<Item = ArrayRef>,
) -> Self {
Self::try_new(type_ids, variants, children).vortex_expect("UnionArray construction failed")
}
pub fn try_new(
type_ids: ArrayRef,
variants: UnionVariants,
children: impl IntoIterator<Item = ArrayRef>,
) -> VortexResult<Self> {
vortex_ensure!(
matches!(type_ids.dtype(), DType::Primitive(PType::U8, _)),
"UnionArray type_ids must be u8, got {}",
type_ids.dtype()
);
Array::try_from_parts(make_union_parts(type_ids, variants, children))
}
pub unsafe fn new_unchecked(
type_ids: ArrayRef,
variants: UnionVariants,
children: impl IntoIterator<Item = ArrayRef>,
) -> Self {
unsafe { Array::from_parts_unchecked(make_union_parts(type_ids, variants, children)) }
}
pub fn into_data_parts(self) -> UnionDataParts {
let variants = self.variants().clone();
let type_ids = self.type_ids().clone();
let children = self.iter_children().cloned().collect();
UnionDataParts {
variants,
type_ids,
children,
}
}
pub fn constant(scalar: &Scalar, len: usize) -> VortexResult<Self> {
let union = scalar
.as_union_opt()
.ok_or_else(|| vortex_err!("Expected a union scalar, got {}", scalar.dtype()))?;
let variants = union.variants().clone();
let nullability = union.nullability();
let type_ids = match union.type_id() {
Some(type_id) => Scalar::primitive(type_id, nullability),
None => Scalar::null(union_type_ids_dtype(nullability)),
};
let selected = union.child_index().zip(union.child());
let children = variants
.variants()
.enumerate()
.map(|(index, dtype)| {
let value = match &selected {
Some((selected_index, child)) if *selected_index == index => child.clone(),
_ => Scalar::default_value(&dtype),
};
ConstantArray::new(value, len).into_array()
})
.collect::<Vec<_>>();
Self::try_new(
ConstantArray::new(type_ids, len).into_array(),
variants,
children,
)
}
pub(crate) fn empty(variants: UnionVariants, nullability: Nullability) -> Self {
let type_ids = PrimitiveArray::empty::<u8>(nullability).into_array();
let children: Vec<_> = variants
.variants()
.map(|dtype| crate::Canonical::empty(&dtype).into_array())
.collect();
Self::new(type_ids, variants, children)
}
}