use std::marker::PhantomData;
use crate::format::messages::attribute::AttributeMessage;
use crate::error::{Hdf5Error, Result};
use crate::file::{borrow_inner_mut, clone_inner, H5FileInner, SharedInner};
use crate::types::VarLenUnicode;
pub struct H5Attribute {
file_inner: SharedInner,
ds_index: usize,
name: String,
read_data: Option<Vec<u8>>,
}
impl H5Attribute {
pub(crate) fn new_reader(file_inner: SharedInner, name: String, data: Vec<u8>) -> Self {
Self {
file_inner,
ds_index: usize::MAX,
name,
read_data: Some(data),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn write_scalar(&self, value: &VarLenUnicode) -> Result<()> {
let attr_msg = AttributeMessage::scalar_string(&self.name, &value.0);
let mut inner = borrow_inner_mut(&self.file_inner);
match &mut *inner {
H5FileInner::Writer(writer) => {
writer.add_dataset_attribute(self.ds_index, attr_msg)?;
Ok(())
}
H5FileInner::Reader(_) => Err(Hdf5Error::InvalidState(
"cannot write attributes in read mode".into(),
)),
H5FileInner::Closed => Err(Hdf5Error::InvalidState("file is closed".into())),
}
}
pub fn write_string(&self, value: &str) -> Result<()> {
self.write_scalar(&VarLenUnicode(value.to_string()))
}
pub fn write_numeric<T: crate::types::H5Type>(&self, value: &T) -> Result<()> {
let es = T::element_size();
let raw = unsafe { std::slice::from_raw_parts(value as *const T as *const u8, es) };
let attr_msg = AttributeMessage::scalar_numeric(&self.name, T::hdf5_type(), raw.to_vec());
let mut inner = borrow_inner_mut(&self.file_inner);
match &mut *inner {
H5FileInner::Writer(writer) => {
writer.add_dataset_attribute(self.ds_index, attr_msg)?;
Ok(())
}
H5FileInner::Reader(_) => Err(Hdf5Error::InvalidState(
"cannot write attributes in read mode".into(),
)),
H5FileInner::Closed => Err(Hdf5Error::InvalidState("file is closed".into())),
}
}
pub fn read_numeric<T: crate::types::H5Type>(&self) -> Result<T> {
let data = self
.read_data
.as_ref()
.ok_or_else(|| Hdf5Error::InvalidState("attribute has no read data".into()))?;
let es = T::element_size();
if data.len() < es {
return Err(Hdf5Error::TypeMismatch(format!(
"attribute data {} bytes, need {} for type",
data.len(),
es
)));
}
unsafe {
let mut val = std::mem::MaybeUninit::<T>::uninit();
std::ptr::copy_nonoverlapping(data.as_ptr(), val.as_mut_ptr() as *mut u8, es);
Ok(val.assume_init())
}
}
pub fn read_string(&self) -> Result<String> {
let data = self.read_data.as_ref().ok_or_else(|| {
Hdf5Error::InvalidState("attribute has no read data (write-mode handle?)".into())
})?;
let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
Ok(String::from_utf8_lossy(&data[..end]).to_string())
}
pub fn read_raw(&self) -> Result<Vec<u8>> {
self.read_data.clone().ok_or_else(|| {
Hdf5Error::InvalidState("attribute has no read data (write-mode handle?)".into())
})
}
}
pub struct AttrBuilder<'a, T> {
file_inner: &'a SharedInner,
ds_index: usize,
_shape_set: bool,
_marker: PhantomData<T>,
}
impl<'a, T> AttrBuilder<'a, T> {
pub(crate) fn new(file_inner: &'a SharedInner, ds_index: usize) -> Self {
Self {
file_inner,
ds_index,
_shape_set: false,
_marker: PhantomData,
}
}
#[must_use]
pub fn shape<S>(mut self, _shape: S) -> Self {
self._shape_set = true;
self
}
pub fn create(self, name: &str) -> Result<H5Attribute> {
Ok(H5Attribute {
file_inner: clone_inner(self.file_inner),
ds_index: self.ds_index,
name: name.to_string(),
read_data: None,
})
}
}