1use std::marker::PhantomData;
20
21use crate::format::messages::attribute::AttributeMessage;
22
23use crate::error::{Hdf5Error, Result};
24use crate::file::{borrow_inner_mut, clone_inner, H5FileInner, SharedInner};
25use crate::types::VarLenUnicode;
26
27pub struct H5Attribute {
35 file_inner: SharedInner,
36 ds_index: usize,
37 name: String,
38 read_data: Option<Vec<u8>>,
40}
41
42impl H5Attribute {
43 pub(crate) fn new_reader(file_inner: SharedInner, name: String, data: Vec<u8>) -> Self {
45 Self {
46 file_inner,
47 ds_index: usize::MAX,
48 name,
49 read_data: Some(data),
50 }
51 }
52
53 pub fn name(&self) -> &str {
55 &self.name
56 }
57
58 pub fn write_scalar(&self, value: &VarLenUnicode) -> Result<()> {
63 let attr_msg = AttributeMessage::scalar_string(&self.name, &value.0);
64
65 let mut inner = borrow_inner_mut(&self.file_inner);
66 match &mut *inner {
67 H5FileInner::Writer(writer) => {
68 writer.add_dataset_attribute(self.ds_index, attr_msg)?;
69 Ok(())
70 }
71 H5FileInner::Reader(_) => Err(Hdf5Error::InvalidState(
72 "cannot write attributes in read mode".into(),
73 )),
74 H5FileInner::Closed => Err(Hdf5Error::InvalidState("file is closed".into())),
75 }
76 }
77
78 pub fn write_string(&self, value: &str) -> Result<()> {
80 self.write_scalar(&VarLenUnicode(value.to_string()))
81 }
82
83 pub fn write_numeric<T: crate::types::H5Type>(&self, value: &T) -> Result<()> {
94 let es = T::element_size();
95 let raw = unsafe { std::slice::from_raw_parts(value as *const T as *const u8, es) };
96 let attr_msg = AttributeMessage::scalar_numeric(&self.name, T::hdf5_type(), raw.to_vec());
97
98 let mut inner = borrow_inner_mut(&self.file_inner);
99 match &mut *inner {
100 H5FileInner::Writer(writer) => {
101 writer.add_dataset_attribute(self.ds_index, attr_msg)?;
102 Ok(())
103 }
104 H5FileInner::Reader(_) => Err(Hdf5Error::InvalidState(
105 "cannot write attributes in read mode".into(),
106 )),
107 H5FileInner::Closed => Err(Hdf5Error::InvalidState("file is closed".into())),
108 }
109 }
110
111 pub fn read_numeric<T: crate::types::H5Type>(&self) -> Result<T> {
121 let data = self
122 .read_data
123 .as_ref()
124 .ok_or_else(|| Hdf5Error::InvalidState("attribute has no read data".into()))?;
125 let es = T::element_size();
126 if data.len() < es {
127 return Err(Hdf5Error::TypeMismatch(format!(
128 "attribute data {} bytes, need {} for type",
129 data.len(),
130 es
131 )));
132 }
133 unsafe {
134 let mut val = std::mem::MaybeUninit::<T>::uninit();
135 std::ptr::copy_nonoverlapping(data.as_ptr(), val.as_mut_ptr() as *mut u8, es);
136 Ok(val.assume_init())
137 }
138 }
139
140 pub fn read_string(&self) -> Result<String> {
145 let data = self.read_data.as_ref().ok_or_else(|| {
146 Hdf5Error::InvalidState("attribute has no read data (write-mode handle?)".into())
147 })?;
148 let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
150 Ok(String::from_utf8_lossy(&data[..end]).to_string())
151 }
152
153 pub fn read_raw(&self) -> Result<Vec<u8>> {
155 self.read_data.clone().ok_or_else(|| {
156 Hdf5Error::InvalidState("attribute has no read data (write-mode handle?)".into())
157 })
158 }
159}
160
161pub struct AttrBuilder<'a, T> {
165 file_inner: &'a SharedInner,
166 ds_index: usize,
167 _shape_set: bool,
168 _marker: PhantomData<T>,
169}
170
171impl<'a, T> AttrBuilder<'a, T> {
172 pub(crate) fn new(file_inner: &'a SharedInner, ds_index: usize) -> Self {
173 Self {
174 file_inner,
175 ds_index,
176 _shape_set: false,
177 _marker: PhantomData,
178 }
179 }
180
181 #[must_use]
183 pub fn shape<S>(mut self, _shape: S) -> Self {
184 self._shape_set = true;
186 self
187 }
188
189 pub fn create(self, name: &str) -> Result<H5Attribute> {
194 Ok(H5Attribute {
195 file_inner: clone_inner(self.file_inner),
196 ds_index: self.ds_index,
197 name: name.to_string(),
198 read_data: None,
199 })
200 }
201}