1use crate::datatype::Datatype;
6use crate::error::{Hdf5Error, Result};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum AttributeValue {
13 Int8(i8),
15 UInt8(u8),
17 Int16(i16),
19 UInt16(u16),
21 Int32(i32),
23 UInt32(u32),
25 Int64(i64),
27 UInt64(u64),
29 Float32(f32),
31 Float64(f64),
33 String(String),
35 Int8Array(Vec<i8>),
37 UInt8Array(Vec<u8>),
39 Int16Array(Vec<i16>),
41 UInt16Array(Vec<u16>),
43 Int32Array(Vec<i32>),
45 UInt32Array(Vec<u32>),
47 Int64Array(Vec<i64>),
49 UInt64Array(Vec<u64>),
51 Float32Array(Vec<f32>),
53 Float64Array(Vec<f64>),
55 StringArray(Vec<String>),
57}
58
59impl AttributeValue {
60 pub fn datatype(&self) -> Datatype {
62 match self {
63 Self::Int8(_) | Self::Int8Array(_) => Datatype::Int8,
64 Self::UInt8(_) | Self::UInt8Array(_) => Datatype::UInt8,
65 Self::Int16(_) | Self::Int16Array(_) => Datatype::Int16,
66 Self::UInt16(_) | Self::UInt16Array(_) => Datatype::UInt16,
67 Self::Int32(_) | Self::Int32Array(_) => Datatype::Int32,
68 Self::UInt32(_) | Self::UInt32Array(_) => Datatype::UInt32,
69 Self::Int64(_) | Self::Int64Array(_) => Datatype::Int64,
70 Self::UInt64(_) | Self::UInt64Array(_) => Datatype::UInt64,
71 Self::Float32(_) | Self::Float32Array(_) => Datatype::Float32,
72 Self::Float64(_) | Self::Float64Array(_) => Datatype::Float64,
73 Self::String(s) => Datatype::FixedString {
74 length: s.len(),
75 padding: crate::datatype::StringPadding::NullTerminated,
76 },
77 Self::StringArray(arr) => Datatype::FixedString {
78 length: arr.first().map(|s| s.len()).unwrap_or(0),
79 padding: crate::datatype::StringPadding::NullTerminated,
80 },
81 }
82 }
83
84 pub fn shape(&self) -> Vec<usize> {
86 match self {
87 Self::Int8(_)
88 | Self::UInt8(_)
89 | Self::Int16(_)
90 | Self::UInt16(_)
91 | Self::Int32(_)
92 | Self::UInt32(_)
93 | Self::Int64(_)
94 | Self::UInt64(_)
95 | Self::Float32(_)
96 | Self::Float64(_)
97 | Self::String(_) => vec![],
98 Self::Int8Array(v) => vec![v.len()],
99 Self::UInt8Array(v) => vec![v.len()],
100 Self::Int16Array(v) => vec![v.len()],
101 Self::UInt16Array(v) => vec![v.len()],
102 Self::Int32Array(v) => vec![v.len()],
103 Self::UInt32Array(v) => vec![v.len()],
104 Self::Int64Array(v) => vec![v.len()],
105 Self::UInt64Array(v) => vec![v.len()],
106 Self::Float32Array(v) => vec![v.len()],
107 Self::Float64Array(v) => vec![v.len()],
108 Self::StringArray(v) => vec![v.len()],
109 }
110 }
111
112 pub fn as_i32(&self) -> Result<i32> {
114 match self {
115 Self::Int8(v) => Ok(*v as i32),
116 Self::UInt8(v) => Ok(*v as i32),
117 Self::Int16(v) => Ok(*v as i32),
118 Self::UInt16(v) => Ok(*v as i32),
119 Self::Int32(v) => Ok(*v),
120 Self::UInt32(v) => {
121 i32::try_from(*v).map_err(|_| Hdf5Error::type_conversion("u32", "i32"))
122 }
123 Self::Int64(v) => {
124 i32::try_from(*v).map_err(|_| Hdf5Error::type_conversion("i64", "i32"))
125 }
126 Self::UInt64(v) => {
127 i32::try_from(*v).map_err(|_| Hdf5Error::type_conversion("u64", "i32"))
128 }
129 _ => Err(Hdf5Error::type_conversion(self.datatype().name(), "i32")),
130 }
131 }
132
133 pub fn as_f64(&self) -> Result<f64> {
135 match self {
136 Self::Int8(v) => Ok(*v as f64),
137 Self::UInt8(v) => Ok(*v as f64),
138 Self::Int16(v) => Ok(*v as f64),
139 Self::UInt16(v) => Ok(*v as f64),
140 Self::Int32(v) => Ok(*v as f64),
141 Self::UInt32(v) => Ok(*v as f64),
142 Self::Int64(v) => Ok(*v as f64),
143 Self::UInt64(v) => Ok(*v as f64),
144 Self::Float32(v) => Ok(*v as f64),
145 Self::Float64(v) => Ok(*v),
146 _ => Err(Hdf5Error::type_conversion(self.datatype().name(), "f64")),
147 }
148 }
149
150 pub fn as_string(&self) -> Result<String> {
152 match self {
153 Self::String(s) => Ok(s.clone()),
154 Self::Int8(v) => Ok(v.to_string()),
155 Self::UInt8(v) => Ok(v.to_string()),
156 Self::Int16(v) => Ok(v.to_string()),
157 Self::UInt16(v) => Ok(v.to_string()),
158 Self::Int32(v) => Ok(v.to_string()),
159 Self::UInt32(v) => Ok(v.to_string()),
160 Self::Int64(v) => Ok(v.to_string()),
161 Self::UInt64(v) => Ok(v.to_string()),
162 Self::Float32(v) => Ok(v.to_string()),
163 Self::Float64(v) => Ok(v.to_string()),
164 _ => Err(Hdf5Error::type_conversion(self.datatype().name(), "string")),
165 }
166 }
167
168 pub fn as_i32_array(&self) -> Result<Vec<i32>> {
170 match self {
171 Self::Int32Array(v) => Ok(v.clone()),
172 Self::Int8Array(v) => Ok(v.iter().map(|&x| x as i32).collect()),
173 Self::UInt8Array(v) => Ok(v.iter().map(|&x| x as i32).collect()),
174 Self::Int16Array(v) => Ok(v.iter().map(|&x| x as i32).collect()),
175 Self::UInt16Array(v) => Ok(v.iter().map(|&x| x as i32).collect()),
176 _ => Err(Hdf5Error::type_conversion(self.datatype().name(), "i32[]")),
177 }
178 }
179
180 pub fn as_f64_array(&self) -> Result<Vec<f64>> {
182 match self {
183 Self::Float64Array(v) => Ok(v.clone()),
184 Self::Float32Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
185 Self::Int8Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
186 Self::UInt8Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
187 Self::Int16Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
188 Self::UInt16Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
189 Self::Int32Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
190 Self::UInt32Array(v) => Ok(v.iter().map(|&x| x as f64).collect()),
191 _ => Err(Hdf5Error::type_conversion(self.datatype().name(), "f64[]")),
192 }
193 }
194
195 pub fn as_string_array(&self) -> Result<Vec<String>> {
197 match self {
198 Self::StringArray(v) => Ok(v.clone()),
199 _ => Err(Hdf5Error::type_conversion(
200 self.datatype().name(),
201 "string[]",
202 )),
203 }
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209pub struct Attribute {
210 name: String,
212 value: AttributeValue,
214}
215
216impl Attribute {
217 pub fn new(name: String, value: AttributeValue) -> Self {
219 Self { name, value }
220 }
221
222 pub fn i32(name: impl Into<String>, value: i32) -> Self {
224 Self::new(name.into(), AttributeValue::Int32(value))
225 }
226
227 pub fn f64(name: impl Into<String>, value: f64) -> Self {
229 Self::new(name.into(), AttributeValue::Float64(value))
230 }
231
232 pub fn string(name: impl Into<String>, value: impl Into<String>) -> Self {
234 Self::new(name.into(), AttributeValue::String(value.into()))
235 }
236
237 pub fn i32_array(name: impl Into<String>, value: Vec<i32>) -> Self {
239 Self::new(name.into(), AttributeValue::Int32Array(value))
240 }
241
242 pub fn f64_array(name: impl Into<String>, value: Vec<f64>) -> Self {
244 Self::new(name.into(), AttributeValue::Float64Array(value))
245 }
246
247 pub fn string_array(name: impl Into<String>, value: Vec<String>) -> Self {
249 Self::new(name.into(), AttributeValue::StringArray(value))
250 }
251
252 pub fn name(&self) -> &str {
254 &self.name
255 }
256
257 pub fn value(&self) -> &AttributeValue {
259 &self.value
260 }
261
262 pub fn datatype(&self) -> Datatype {
264 self.value.datatype()
265 }
266
267 pub fn shape(&self) -> Vec<usize> {
269 self.value.shape()
270 }
271
272 pub fn as_i32(&self) -> Result<i32> {
274 self.value.as_i32()
275 }
276
277 pub fn as_f64(&self) -> Result<f64> {
279 self.value.as_f64()
280 }
281
282 pub fn as_string(&self) -> Result<String> {
284 self.value.as_string()
285 }
286
287 pub fn as_i32_array(&self) -> Result<Vec<i32>> {
289 self.value.as_i32_array()
290 }
291
292 pub fn as_f64_array(&self) -> Result<Vec<f64>> {
294 self.value.as_f64_array()
295 }
296
297 pub fn as_string_array(&self) -> Result<Vec<String>> {
299 self.value.as_string_array()
300 }
301}
302
303#[derive(Debug, Clone, Default, Serialize, Deserialize)]
305pub struct Attributes {
306 attributes: HashMap<String, Attribute>,
308}
309
310impl Attributes {
311 pub fn new() -> Self {
313 Self {
314 attributes: HashMap::new(),
315 }
316 }
317
318 pub fn add(&mut self, attribute: Attribute) {
320 self.attributes
321 .insert(attribute.name().to_string(), attribute);
322 }
323
324 pub fn get(&self, name: &str) -> Result<&Attribute> {
326 self.attributes
327 .get(name)
328 .ok_or_else(|| Hdf5Error::attribute_not_found(name))
329 }
330
331 pub fn contains(&self, name: &str) -> bool {
333 self.attributes.contains_key(name)
334 }
335
336 pub fn names(&self) -> Vec<&str> {
338 self.attributes.keys().map(|s| s.as_str()).collect()
339 }
340
341 pub fn len(&self) -> usize {
343 self.attributes.len()
344 }
345
346 pub fn is_empty(&self) -> bool {
348 self.attributes.is_empty()
349 }
350
351 pub fn iter(&self) -> impl Iterator<Item = &Attribute> {
353 self.attributes.values()
354 }
355
356 pub fn remove(&mut self, name: &str) -> Option<Attribute> {
358 self.attributes.remove(name)
359 }
360
361 pub fn clear(&mut self) {
363 self.attributes.clear();
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_attribute_value_i32() {
373 let value = AttributeValue::Int32(42);
374 assert_eq!(value.as_i32().ok(), Some(42));
375 assert_eq!(value.as_f64().ok(), Some(42.0));
376 assert_eq!(value.as_string().ok(), Some("42".to_string()));
377 assert_eq!(value.shape(), Vec::<usize>::new());
378 }
379
380 #[test]
381 fn test_attribute_value_f64() {
382 let value = AttributeValue::Float64(3.125);
383 assert_eq!(value.as_f64().ok(), Some(3.125));
384 assert!(value.as_i32().is_err());
385 assert_eq!(value.shape(), Vec::<usize>::new());
386 }
387
388 #[test]
389 fn test_attribute_value_string() {
390 let value = AttributeValue::String("hello".to_string());
391 assert_eq!(value.as_string().ok(), Some("hello".to_string()));
392 assert!(value.as_i32().is_err());
393 assert_eq!(value.shape(), Vec::<usize>::new());
394 }
395
396 #[test]
397 fn test_attribute_value_array() {
398 let value = AttributeValue::Int32Array(vec![1, 2, 3, 4, 5]);
399 assert_eq!(value.as_i32_array().ok(), Some(vec![1, 2, 3, 4, 5]));
400 assert_eq!(
401 value.as_f64_array().ok(),
402 Some(vec![1.0, 2.0, 3.0, 4.0, 5.0])
403 );
404 assert_eq!(value.shape(), vec![5]);
405 }
406
407 #[test]
408 fn test_attribute_creation() {
409 let attr = Attribute::i32("version", 1);
410 assert_eq!(attr.name(), "version");
411 assert_eq!(attr.as_i32().ok(), Some(1));
412
413 let attr = Attribute::f64("scale", 0.5);
414 assert_eq!(attr.name(), "scale");
415 assert_eq!(attr.as_f64().ok(), Some(0.5));
416
417 let attr = Attribute::string("units", "meters");
418 assert_eq!(attr.name(), "units");
419 assert_eq!(attr.as_string().ok(), Some("meters".to_string()));
420 }
421
422 #[test]
423 fn test_attributes_collection() {
424 let mut attrs = Attributes::new();
425 assert!(attrs.is_empty());
426
427 attrs.add(Attribute::i32("version", 1));
428 attrs.add(Attribute::f64("scale", 0.5));
429 attrs.add(Attribute::string("units", "meters"));
430
431 assert_eq!(attrs.len(), 3);
432 assert!(attrs.contains("version"));
433 assert!(attrs.contains("scale"));
434 assert!(attrs.contains("units"));
435 assert!(!attrs.contains("nonexistent"));
436
437 let version = attrs.get("version").expect("version not found");
438 assert_eq!(version.as_i32().ok(), Some(1));
439
440 let scale = attrs.get("scale").expect("scale not found");
441 assert_eq!(scale.as_f64().ok(), Some(0.5));
442
443 let units = attrs.get("units").expect("units not found");
444 assert_eq!(units.as_string().ok(), Some("meters".to_string()));
445
446 assert!(attrs.get("nonexistent").is_err());
447
448 attrs.remove("version");
449 assert_eq!(attrs.len(), 2);
450 assert!(!attrs.contains("version"));
451
452 attrs.clear();
453 assert!(attrs.is_empty());
454 }
455
456 #[test]
457 fn test_attributes_names() {
458 let mut attrs = Attributes::new();
459 attrs.add(Attribute::i32("a", 1));
460 attrs.add(Attribute::i32("b", 2));
461 attrs.add(Attribute::i32("c", 3));
462
463 let mut names = attrs.names();
464 names.sort();
465 assert_eq!(names, vec!["a", "b", "c"]);
466 }
467}