Skip to main content

rust_hdf5/
types.rs

1//! Mapping from Rust types to HDF5 datatype messages.
2
3use crate::format::messages::datatype::DatatypeMessage;
4
5/// Trait for Rust types that have a corresponding HDF5 datatype.
6///
7/// Implement this for any `Copy + 'static` type that can be stored in an HDF5
8/// dataset. The library provides implementations for all standard numeric types.
9pub trait H5Type: Sized + Copy + 'static {
10    /// Return the HDF5 datatype message describing this type.
11    fn hdf5_type() -> DatatypeMessage;
12
13    /// Return the size of a single element in bytes.
14    fn element_size() -> usize;
15}
16
17impl H5Type for u8 {
18    fn hdf5_type() -> DatatypeMessage {
19        DatatypeMessage::u8_type()
20    }
21    fn element_size() -> usize {
22        1
23    }
24}
25
26impl H5Type for i8 {
27    fn hdf5_type() -> DatatypeMessage {
28        DatatypeMessage::i8_type()
29    }
30    fn element_size() -> usize {
31        1
32    }
33}
34
35impl H5Type for u16 {
36    fn hdf5_type() -> DatatypeMessage {
37        DatatypeMessage::u16_type()
38    }
39    fn element_size() -> usize {
40        2
41    }
42}
43
44impl H5Type for i16 {
45    fn hdf5_type() -> DatatypeMessage {
46        DatatypeMessage::i16_type()
47    }
48    fn element_size() -> usize {
49        2
50    }
51}
52
53impl H5Type for u32 {
54    fn hdf5_type() -> DatatypeMessage {
55        DatatypeMessage::u32_type()
56    }
57    fn element_size() -> usize {
58        4
59    }
60}
61
62impl H5Type for i32 {
63    fn hdf5_type() -> DatatypeMessage {
64        DatatypeMessage::i32_type()
65    }
66    fn element_size() -> usize {
67        4
68    }
69}
70
71impl H5Type for u64 {
72    fn hdf5_type() -> DatatypeMessage {
73        DatatypeMessage::u64_type()
74    }
75    fn element_size() -> usize {
76        8
77    }
78}
79
80impl H5Type for i64 {
81    fn hdf5_type() -> DatatypeMessage {
82        DatatypeMessage::i64_type()
83    }
84    fn element_size() -> usize {
85        8
86    }
87}
88
89impl H5Type for f32 {
90    fn hdf5_type() -> DatatypeMessage {
91        DatatypeMessage::f32_type()
92    }
93    fn element_size() -> usize {
94        4
95    }
96}
97
98impl H5Type for f64 {
99    fn hdf5_type() -> DatatypeMessage {
100        DatatypeMessage::f64_type()
101    }
102    fn element_size() -> usize {
103        8
104    }
105}
106
107/// HDF5 boolean type, stored as u8 (0=false, 1=true).
108///
109/// This is a `Copy` type that can be used with `H5Type`:
110/// ```
111/// use rust_hdf5::types::HBool;
112/// let t: HBool = true.into();
113/// let b: bool = t.into();
114/// ```
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
116#[repr(transparent)]
117pub struct HBool(pub u8);
118
119impl From<bool> for HBool {
120    fn from(b: bool) -> Self {
121        Self(b as u8)
122    }
123}
124
125impl From<HBool> for bool {
126    fn from(h: HBool) -> Self {
127        h.0 != 0
128    }
129}
130
131impl H5Type for HBool {
132    fn hdf5_type() -> DatatypeMessage {
133        DatatypeMessage::bool_type()
134    }
135    fn element_size() -> usize {
136        1
137    }
138}
139
140/// Complex number with f32 real and imaginary parts (8 bytes total).
141#[derive(Debug, Clone, Copy, PartialEq, Default)]
142#[repr(C)]
143pub struct Complex32 {
144    pub re: f32,
145    pub im: f32,
146}
147
148impl H5Type for Complex32 {
149    fn hdf5_type() -> DatatypeMessage {
150        use crate::format::messages::datatype::CompoundMember;
151        DatatypeMessage::Compound {
152            size: 8,
153            members: vec![
154                CompoundMember {
155                    name: "r".to_string(),
156                    offset: 0,
157                    datatype: DatatypeMessage::f32_type(),
158                },
159                CompoundMember {
160                    name: "i".to_string(),
161                    offset: 4,
162                    datatype: DatatypeMessage::f32_type(),
163                },
164            ],
165        }
166    }
167    fn element_size() -> usize {
168        8
169    }
170}
171
172/// Complex number with f64 real and imaginary parts (16 bytes total).
173#[derive(Debug, Clone, Copy, PartialEq, Default)]
174#[repr(C)]
175pub struct Complex64 {
176    pub re: f64,
177    pub im: f64,
178}
179
180impl H5Type for Complex64 {
181    fn hdf5_type() -> DatatypeMessage {
182        use crate::format::messages::datatype::CompoundMember;
183        DatatypeMessage::Compound {
184            size: 16,
185            members: vec![
186                CompoundMember {
187                    name: "r".to_string(),
188                    offset: 0,
189                    datatype: DatatypeMessage::f64_type(),
190                },
191                CompoundMember {
192                    name: "i".to_string(),
193                    offset: 8,
194                    datatype: DatatypeMessage::f64_type(),
195                },
196            ],
197        }
198    }
199    fn element_size() -> usize {
200        16
201    }
202}
203
204/// A description of a compound (struct) type for use with HDF5 datasets.
205///
206/// Users can build compound types manually to describe structured data.
207///
208/// # Example
209///
210/// ```
211/// use rust_hdf5::types::CompoundType;
212/// use rust_hdf5::format::messages::datatype::{DatatypeMessage, CompoundMember};
213///
214/// let ct = CompoundType {
215///     members: vec![
216///         ("x".to_string(), DatatypeMessage::f32_type(), 0),
217///         ("y".to_string(), DatatypeMessage::f32_type(), 4),
218///     ],
219///     total_size: 8,
220/// };
221/// let dt = ct.to_datatype();
222/// assert_eq!(dt.element_size(), 8);
223/// ```
224#[derive(Debug, Clone)]
225pub struct CompoundType {
226    /// Members: (name, datatype, byte_offset).
227    pub members: Vec<(String, DatatypeMessage, u32)>,
228    /// Total size of the compound element in bytes.
229    pub total_size: u32,
230}
231
232impl CompoundType {
233    /// Convert this compound type description to a `DatatypeMessage`.
234    pub fn to_datatype(&self) -> DatatypeMessage {
235        use crate::format::messages::datatype::CompoundMember;
236
237        let members = self
238            .members
239            .iter()
240            .map(|(name, dt, offset)| CompoundMember {
241                name: name.clone(),
242                offset: *offset,
243                datatype: dt.clone(),
244            })
245            .collect();
246
247        DatatypeMessage::Compound {
248            size: self.total_size,
249            members,
250        }
251    }
252}
253
254/// A variable-length Unicode string type.
255///
256/// This type is used as a marker for the `new_attr` builder API.
257/// Internally, attributes with this type use fixed-length string encoding
258/// (the string value determines the size), avoiding the complexity of the
259/// global heap while maintaining API compatibility with hdf5-metno.
260///
261/// # Example
262///
263/// ```no_run
264/// use rust_hdf5::types::VarLenUnicode;
265///
266/// let s: VarLenUnicode = "hello".parse().unwrap();
267/// assert_eq!(s.0, "hello");
268/// ```
269#[derive(Debug, Clone, PartialEq, Eq, Default)]
270pub struct VarLenUnicode(pub String);
271
272impl std::str::FromStr for VarLenUnicode {
273    type Err = std::convert::Infallible;
274    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
275        Ok(Self(s.to_string()))
276    }
277}
278
279impl From<String> for VarLenUnicode {
280    fn from(s: String) -> Self {
281        Self(s)
282    }
283}
284
285impl From<&str> for VarLenUnicode {
286    fn from(s: &str) -> Self {
287        Self(s.to_string())
288    }
289}
290
291impl From<VarLenUnicode> for String {
292    fn from(v: VarLenUnicode) -> Self {
293        v.0
294    }
295}
296
297impl std::fmt::Display for VarLenUnicode {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299        write!(f, "{}", self.0)
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306
307    #[test]
308    fn u8_type_matches() {
309        assert_eq!(u8::element_size(), 1);
310        assert_eq!(u8::hdf5_type(), DatatypeMessage::u8_type());
311    }
312
313    #[test]
314    fn i8_type_matches() {
315        assert_eq!(i8::element_size(), 1);
316        assert_eq!(i8::hdf5_type(), DatatypeMessage::i8_type());
317    }
318
319    #[test]
320    fn u16_type_matches() {
321        assert_eq!(u16::element_size(), 2);
322        assert_eq!(u16::hdf5_type(), DatatypeMessage::u16_type());
323    }
324
325    #[test]
326    fn i16_type_matches() {
327        assert_eq!(i16::element_size(), 2);
328        assert_eq!(i16::hdf5_type(), DatatypeMessage::i16_type());
329    }
330
331    #[test]
332    fn u32_type_matches() {
333        assert_eq!(u32::element_size(), 4);
334        assert_eq!(u32::hdf5_type(), DatatypeMessage::u32_type());
335    }
336
337    #[test]
338    fn i32_type_matches() {
339        assert_eq!(i32::element_size(), 4);
340        assert_eq!(i32::hdf5_type(), DatatypeMessage::i32_type());
341    }
342
343    #[test]
344    fn u64_type_matches() {
345        assert_eq!(u64::element_size(), 8);
346        assert_eq!(u64::hdf5_type(), DatatypeMessage::u64_type());
347    }
348
349    #[test]
350    fn i64_type_matches() {
351        assert_eq!(i64::element_size(), 8);
352        assert_eq!(i64::hdf5_type(), DatatypeMessage::i64_type());
353    }
354
355    #[test]
356    fn f32_type_matches() {
357        assert_eq!(f32::element_size(), 4);
358        assert_eq!(f32::hdf5_type(), DatatypeMessage::f32_type());
359    }
360
361    #[test]
362    fn f64_type_matches() {
363        assert_eq!(f64::element_size(), 8);
364        assert_eq!(f64::hdf5_type(), DatatypeMessage::f64_type());
365    }
366
367    #[test]
368    fn element_size_matches_std_mem() {
369        assert_eq!(u8::element_size(), std::mem::size_of::<u8>());
370        assert_eq!(i16::element_size(), std::mem::size_of::<i16>());
371        assert_eq!(u32::element_size(), std::mem::size_of::<u32>());
372        assert_eq!(f64::element_size(), std::mem::size_of::<f64>());
373    }
374}