use half::{bf16, f16};
use serde_json::Number;
use zarrs_data_type::FillValue;
use zarrs_metadata::FillValueMetadata;
use zarrs_metadata::v3::{ZARR_NAN_BF16, ZARR_NAN_F16, ZARR_NAN_F32, ZARR_NAN_F64};
#[derive(Debug, PartialEq, Clone)]
pub struct ArrayBuilderFillValue(pub(crate) ArrayBuilderFillValueImpl);
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum ArrayBuilderFillValueImpl {
FillValue(FillValue),
Metadata(FillValueMetadata),
}
impl From<FillValue> for ArrayBuilderFillValue {
fn from(value: FillValue) -> Self {
Self(ArrayBuilderFillValueImpl::FillValue(value))
}
}
impl From<FillValueMetadata> for ArrayBuilderFillValue {
fn from(value: FillValueMetadata) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(value))
}
}
impl From<&[u8]> for ArrayBuilderFillValue {
fn from(value: &[u8]) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
FillValueMetadata::Array(value.iter().map(|v| FillValueMetadata::from(*v)).collect()),
))
}
}
impl From<Vec<u8>> for ArrayBuilderFillValue {
fn from(value: Vec<u8>) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
FillValueMetadata::Array(value.into_iter().map(FillValueMetadata::from).collect()),
))
}
}
impl From<&str> for ArrayBuilderFillValue {
fn from(value: &str) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
FillValueMetadata::String(value.to_string()),
))
}
}
impl<const N: usize> From<[FillValueMetadata; N]> for ArrayBuilderFillValue {
fn from(value: [FillValueMetadata; N]) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
FillValueMetadata::Array(value.to_vec()),
))
}
}
impl<const N: usize> From<&[FillValueMetadata; N]> for ArrayBuilderFillValue {
fn from(value: &[FillValueMetadata; N]) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
FillValueMetadata::Array(value.to_vec()),
))
}
}
macro_rules! impl_from_for_int_fill_value_metadata_v3 {
($($t:ty),*) => {
$(
impl From<$t> for ArrayBuilderFillValue {
fn from(value: $t) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(FillValueMetadata::Number(Number::from(value))))
}
}
)*
};
}
impl_from_for_int_fill_value_metadata_v3!(u8, u16, u32, u64, i8, i16, i32, i64);
macro_rules! impl_from_for_float_fill_value_metadata_v3 {
($type:ty, $nan_value:expr, $value_conversion:expr) => {
impl From<$type> for ArrayBuilderFillValue {
fn from(value: $type) -> Self {
Self(ArrayBuilderFillValueImpl::Metadata(
if value.is_infinite() && value.is_sign_positive() {
FillValueMetadata::String("Infinity".to_string())
} else if value.is_infinite() && value.is_sign_negative() {
FillValueMetadata::String("-Infinity".to_string())
} else if value.to_bits() == $nan_value.to_bits() {
FillValueMetadata::String("NaN".to_string())
} else if value.is_nan() {
FillValueMetadata::String(bytes_to_hex_string(&value.to_be_bytes()))
} else {
FillValueMetadata::Number(
Number::from_f64($value_conversion(value))
.expect("already checked finite"),
)
},
))
}
}
};
}
impl_from_for_float_fill_value_metadata_v3!(bf16, ZARR_NAN_BF16, f64::from);
impl_from_for_float_fill_value_metadata_v3!(f16, ZARR_NAN_F16, f64::from);
impl_from_for_float_fill_value_metadata_v3!(f32, ZARR_NAN_F32, f64::from);
impl_from_for_float_fill_value_metadata_v3!(f64, ZARR_NAN_F64, |v| v);
fn bytes_to_hex_string(v: &[u8]) -> String {
let mut string = String::with_capacity(2 + v.len() * 2);
string.push('0');
string.push('x');
for byte in v {
string.push(char::from_digit((byte / 16).into(), 16).unwrap());
string.push(char::from_digit((byte % 16).into(), 16).unwrap());
}
string
}