use arrow::array::{Array, ArrayRef, new_null_array};
use arrow::datatypes::DataType;
use crate::attributes::Attributes;
use crate::dimensions::Dimensions;
use crate::error::{Result, nd_err};
#[derive(Debug, Clone)]
pub struct NdArrowArray {
values: ArrayRef,
dims: Dimensions,
attrs: Attributes,
}
impl PartialEq for NdArrowArray {
fn eq(&self, other: &Self) -> bool {
self.dims == other.dims
&& self.values.as_ref() == other.values.as_ref()
&& self.attrs == other.attrs
}
}
impl NdArrowArray {
pub fn try_new(values: ArrayRef, dims: Dimensions) -> Result<Self> {
if dims.checked_num_elements() != Some(values.len()) {
return Err(nd_err!(
ShapeMismatch,
"values length {} does not match dimensions {dims} ({} elements)",
values.len(),
dims.num_elements()
));
}
Ok(Self::new_unchecked(values, dims, Attributes::new()))
}
pub fn try_new_scalar(value: ArrayRef) -> Result<Self> {
Self::try_new(value, Dimensions::scalar())
}
pub fn new_null_scalar(data_type: &DataType) -> Self {
Self::new_unchecked(
new_null_array(data_type, 1),
Dimensions::scalar(),
Attributes::new(),
)
}
pub(crate) fn new_unchecked(values: ArrayRef, dims: Dimensions, attrs: Attributes) -> Self {
debug_assert_eq!(dims.num_elements(), values.len());
Self {
values,
dims,
attrs,
}
}
pub fn with_attributes(mut self, attrs: Attributes) -> Self {
self.attrs = attrs;
self
}
pub fn values(&self) -> &ArrayRef {
&self.values
}
pub fn dims(&self) -> &Dimensions {
&self.dims
}
pub fn attributes(&self) -> &Attributes {
&self.attrs
}
pub fn attributes_mut(&mut self) -> &mut Attributes {
&mut self.attrs
}
pub fn data_type(&self) -> &DataType {
self.values.data_type()
}
pub fn shape(&self) -> Vec<usize> {
self.dims.shape()
}
pub fn rank(&self) -> usize {
self.dims.rank()
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn is_scalar(&self) -> bool {
self.dims.is_scalar()
}
pub fn reshape(self, dims: Dimensions) -> Result<Self> {
let attrs = self.attrs;
Ok(Self::try_new(self.values, dims)?.with_attributes(attrs))
}
pub fn into_parts(self) -> (ArrayRef, Dimensions, Attributes) {
(self.values, self.dims, self.attrs)
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use arrow::array::Int32Array;
use super::*;
#[test]
fn len_mismatch_rejected() {
let dims = Dimensions::try_new([("x", 2)]).unwrap();
let result = NdArrowArray::try_new(Arc::new(Int32Array::from(vec![1, 2, 3])), dims);
assert!(result.is_err());
}
#[test]
fn reshape_keeps_values_and_attributes() {
let a = NdArrowArray::try_new(
Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6])),
Dimensions::try_new([("x", 6)]).unwrap(),
)
.unwrap()
.with_attributes(Attributes::new().with("units", "m"));
let b = a
.clone()
.reshape(Dimensions::try_new([("y", 2), ("x", 3)]).unwrap())
.unwrap();
assert_eq!(b.shape(), vec![2, 3]);
assert_eq!(b.values(), a.values());
assert_eq!(b.attributes().get_str("units"), Some("m"));
assert!(a.reshape(Dimensions::try_new([("x", 4)]).unwrap()).is_err());
}
}