#![warn(missing_docs)]
use arrow::array::{
Array, Float16Array, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Int64Array,
UInt8Array, UInt16Array, UInt32Array, UInt64Array,
};
use arrow::datatypes::DataType;
use eyre::{ContextCompat, Result, eyre};
use num::NumCast;
#[cfg(feature = "arrow-v58")]
pub mod ffi_bridge;
mod from_impls;
pub mod internal;
mod into_impls;
pub trait IntoArrow {
fn into_arrow(self) -> DoraArray;
}
#[derive(Debug, Clone)]
pub struct DoraArray(arrow::array::ArrayRef);
impl DoraArray {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn null_count(&self) -> usize {
self.0.null_count()
}
pub fn type_name(&self) -> String {
format!("{:?}", self.0.data_type())
}
}
#[cfg(feature = "arrow-v59")]
impl DoraArray {
pub fn as_array(&self) -> &arrow::array::ArrayRef {
&self.0
}
pub fn into_inner(self) -> arrow::array::ArrayRef {
self.0
}
pub fn from_array(array: impl arrow::array::Array + 'static) -> Self {
Self(arrow::array::make_array(array.to_data()))
}
}
#[cfg(feature = "arrow-v59")]
impl From<arrow::array::ArrayRef> for DoraArray {
fn from(value: arrow::array::ArrayRef) -> Self {
Self(value)
}
}
#[cfg(feature = "arrow-v59")]
impl From<DoraArray> for arrow::array::ArrayRef {
fn from(value: DoraArray) -> Self {
value.0
}
}
impl IntoArrow for DoraArray {
fn into_arrow(self) -> DoraArray {
self
}
}
macro_rules! register_array_handlers {
($(($variant:path, $array_type:ty, $type_name:expr)),* $(,)?) => {
pub fn into_vec<T>(data: &DoraArray) -> Result<Vec<T>>
where
T: Copy + NumCast + 'static,
{
match data.0.data_type() {
$(
$variant => {
let buffer: &$array_type = data
.0
.as_any()
.downcast_ref()
.context(concat!("series is not ", $type_name))?;
if buffer.null_count() != 0 {
eyre::bail!("array has nulls");
}
let mut result = Vec::with_capacity(buffer.len());
for &v in buffer.values() {
let converted = NumCast::from(v).with_context(|| {
format!("Failed to cast value from {} to target type", $type_name)
})?;
result.push(converted);
}
Ok(result)
}
),*
unsupported_type => Err(eyre!("Unsupported data type for conversion: {:?}", unsupported_type))
}
}
};
}
register_array_handlers! {
(DataType::Float32, Float32Array, "float32"),
(DataType::Float64, Float64Array, "float64"),
(DataType::Int8, Int8Array, "int8"),
(DataType::Int16, Int16Array, "int16"),
(DataType::Int32, Int32Array, "int32"),
(DataType::Int64, Int64Array, "int64"),
(DataType::UInt8, UInt8Array, "uint8"),
(DataType::UInt16, UInt16Array, "uint16"),
(DataType::UInt32, UInt32Array, "uint32"),
(DataType::UInt64, UInt64Array, "uint64"),
(DataType::Float16, Float16Array, "float16"),
}
#[cfg(test)]
mod tests {
use super::*;
use arrow::array::ArrayRef;
use half::f16;
use std::sync::Arc;
fn wrap(array: ArrayRef) -> DoraArray {
internal::from_array_ref(array)
}
#[test]
fn into_vec_supports_all_registered_types() {
macro_rules! assert_round_trip {
($array_type:ty, $rust_type:ty, $values:expr) => {{
let values: Vec<$rust_type> = $values;
let array: ArrayRef = Arc::new(<$array_type>::from(values.clone()));
let data = wrap(array);
let result: Vec<$rust_type> = into_vec(&data).unwrap();
assert_eq!(result, values);
}};
}
assert_round_trip!(Float32Array, f32, vec![1.0, 2.5, -3.0]);
assert_round_trip!(Float64Array, f64, vec![1.0, 2.5, -3.0]);
assert_round_trip!(Int8Array, i8, vec![-1, 2, 3]);
assert_round_trip!(Int16Array, i16, vec![-1, 2, 3]);
assert_round_trip!(Int32Array, i32, vec![-1, 2, 3]);
assert_round_trip!(Int64Array, i64, vec![-1, 2, 3]);
assert_round_trip!(UInt8Array, u8, vec![1, 2, 3]);
assert_round_trip!(UInt16Array, u16, vec![1, 2, 3]);
assert_round_trip!(UInt32Array, u32, vec![1, 2, 3]);
assert_round_trip!(UInt64Array, u64, vec![1, 2, 3]);
let values = vec![f16::from_f32(1.0), f16::from_f32(2.5), f16::from_f32(-3.0)];
let array: ArrayRef = Arc::new(Float16Array::from(values.clone()));
let data = wrap(array);
let result: Vec<f16> = into_vec(&data).unwrap();
assert_eq!(result, values);
}
#[test]
fn into_vec_handles_uint64() {
let data = wrap(Arc::new(UInt64Array::from(vec![1u64, 2, 3])));
let res: Vec<u64> = into_vec(&data).unwrap();
assert_eq!(res, vec![1u64, 2, 3]);
}
#[test]
fn into_vec_rejects_arrays_with_nulls() {
let array: ArrayRef = Arc::new(UInt64Array::from(vec![Some(1u64), None, Some(3)]));
let data = wrap(array);
let res: Result<Vec<u64>> = into_vec(&data);
assert!(res.is_err());
}
#[test]
fn into_vec_rejects_unsupported_type() {
let array: ArrayRef = Arc::new(arrow::array::BooleanArray::from(vec![true, false]));
let data = wrap(array);
let res: Result<Vec<u8>> = into_vec(&data);
assert!(res.is_err());
}
#[test]
fn ungated_accessors() {
let data = vec![1u64, 2, 3].into_arrow();
assert_eq!(data.len(), 3);
assert!(!data.is_empty());
assert_eq!(data.null_count(), 0);
assert_eq!(data.type_name(), "UInt64");
}
}