1pub mod attributes;
12pub mod dimensions;
13pub mod groups;
14pub mod types;
15pub mod variables;
16
17use std::path::Path;
18
19use hdf5_reader::datatype_api::H5Type;
20use hdf5_reader::Hdf5File;
21use ndarray::ArrayD;
22#[cfg(feature = "rayon")]
23use rayon::ThreadPool;
24
25use crate::error::{Error, Result};
26use crate::types::NcGroup;
27
28macro_rules! dispatch_read_as_f64 {
39 ($dtype:expr, |$T:ident| $read_expr:expr) => {{
40 use crate::types::NcType;
41 match $dtype {
42 NcType::Byte => {
43 type $T = i8;
44 let arr = $read_expr?;
45 Ok(arr.mapv(|v| v as f64))
46 }
47 NcType::Short => {
48 type $T = i16;
49 let arr = $read_expr?;
50 Ok(arr.mapv(|v| v as f64))
51 }
52 NcType::Int => {
53 type $T = i32;
54 let arr = $read_expr?;
55 Ok(arr.mapv(|v| v as f64))
56 }
57 NcType::Float => {
58 type $T = f32;
59 let arr = $read_expr?;
60 Ok(arr.mapv(|v| v as f64))
61 }
62 NcType::Double => {
63 type $T = f64;
64 Ok($read_expr?)
65 }
66 NcType::UByte => {
67 type $T = u8;
68 let arr = $read_expr?;
69 Ok(arr.mapv(|v| v as f64))
70 }
71 NcType::UShort => {
72 type $T = u16;
73 let arr = $read_expr?;
74 Ok(arr.mapv(|v| v as f64))
75 }
76 NcType::UInt => {
77 type $T = u32;
78 let arr = $read_expr?;
79 Ok(arr.mapv(|v| v as f64))
80 }
81 NcType::Int64 => {
82 type $T = i64;
83 let arr = $read_expr?;
84 Ok(arr.mapv(|v| v as f64))
85 }
86 NcType::UInt64 => {
87 type $T = u64;
88 let arr = $read_expr?;
89 Ok(arr.mapv(|v| v as f64))
90 }
91 NcType::Char => Err(Error::TypeMismatch {
92 expected: "numeric type".to_string(),
93 actual: "Char".to_string(),
94 }),
95 NcType::String => Err(Error::TypeMismatch {
96 expected: "numeric type".to_string(),
97 actual: "String".to_string(),
98 }),
99 other => Err(Error::TypeMismatch {
100 expected: "numeric type".to_string(),
101 actual: format!("{:?}", other),
102 }),
103 }
104 }};
105}
106
107pub struct Nc4File {
109 hdf5: Hdf5File,
110 root_group: NcGroup,
111}
112
113impl Nc4File {
114 pub(crate) fn from_hdf5(hdf5: Hdf5File, root_group: NcGroup) -> Self {
116 Nc4File { hdf5, root_group }
117 }
118
119 pub fn open(path: &Path) -> Result<Self> {
121 let hdf5 = Hdf5File::open(path)?;
122 let root_group = groups::build_root_group(&hdf5)?;
123 Ok(Nc4File { hdf5, root_group })
124 }
125
126 pub fn from_bytes(data: &[u8]) -> Result<Self> {
128 let hdf5 = Hdf5File::from_bytes(data)?;
129 let root_group = groups::build_root_group(&hdf5)?;
130 Ok(Nc4File { hdf5, root_group })
131 }
132
133 pub fn root_group(&self) -> &NcGroup {
135 &self.root_group
136 }
137
138 pub fn is_classic_model(&self) -> bool {
143 self.hdf5
144 .root_group()
145 .ok()
146 .and_then(|g| g.attribute("_nc3_strict").ok())
147 .is_some()
148 }
149
150 pub fn read_variable<T: H5Type>(&self, path: &str) -> Result<ArrayD<T>> {
155 let normalized = normalize_dataset_path(path)?;
156 let dataset = self.hdf5.dataset(normalized)?;
157 Ok(dataset.read_array::<T>()?)
158 }
159
160 #[cfg(feature = "rayon")]
161 pub fn read_variable_parallel<T: H5Type>(&self, path: &str) -> Result<ArrayD<T>> {
162 let normalized = normalize_dataset_path(path)?;
163 let dataset = self.hdf5.dataset(normalized)?;
164 Ok(dataset.read_array_parallel::<T>()?)
165 }
166
167 #[cfg(feature = "rayon")]
168 pub fn read_variable_in_pool<T: H5Type>(
169 &self,
170 path: &str,
171 pool: &ThreadPool,
172 ) -> Result<ArrayD<T>> {
173 let normalized = normalize_dataset_path(path)?;
174 let dataset = self.hdf5.dataset(normalized)?;
175 Ok(dataset.read_array_in_pool::<T>(pool)?)
176 }
177}
178
179impl Nc4File {
180 pub fn read_variable_as_f64(&self, path: &str) -> Result<ArrayD<f64>> {
184 let normalized = normalize_dataset_path(path)?;
185 let var = self
186 .root_group
187 .variable(normalized)
188 .ok_or_else(|| Error::VariableNotFound(path.to_string()))?;
189 let dataset = self.hdf5.dataset(normalized)?;
190
191 debug_assert_eq!(dataset.shape(), &var.shape()[..]);
192
193 dispatch_read_as_f64!(&var.dtype, |T| dataset.read_array::<T>())
194 }
195
196 pub fn read_variable_slice_as_f64(
198 &self,
199 path: &str,
200 selection: &crate::types::NcSliceInfo,
201 ) -> Result<ArrayD<f64>> {
202 let normalized = normalize_dataset_path(path)?;
203 let var = self
204 .root_group
205 .variable(normalized)
206 .ok_or_else(|| Error::VariableNotFound(path.to_string()))?;
207 let dataset = self.hdf5.dataset(normalized)?;
208 let hdf5_sel = selection.to_hdf5_slice_info();
209
210 dispatch_read_as_f64!(&var.dtype, |T| dataset.read_slice::<T>(&hdf5_sel))
211 }
212
213 pub fn read_variable_slice<T: H5Type>(
215 &self,
216 path: &str,
217 selection: &crate::types::NcSliceInfo,
218 ) -> Result<ArrayD<T>> {
219 let normalized = normalize_dataset_path(path)?;
220 let dataset = self.hdf5.dataset(normalized)?;
221 let hdf5_sel = selection.to_hdf5_slice_info();
222 Ok(dataset.read_slice::<T>(&hdf5_sel)?)
223 }
224
225 #[cfg(feature = "rayon")]
230 pub fn read_variable_slice_parallel<T: H5Type>(
231 &self,
232 path: &str,
233 selection: &crate::types::NcSliceInfo,
234 ) -> Result<ArrayD<T>> {
235 let normalized = normalize_dataset_path(path)?;
236 let dataset = self.hdf5.dataset(normalized)?;
237 let hdf5_sel = selection.to_hdf5_slice_info();
238 Ok(dataset.read_slice_parallel::<T>(&hdf5_sel)?)
239 }
240}
241
242fn normalize_dataset_path(path: &str) -> Result<&str> {
243 let trimmed = path.trim_matches('/');
244 if trimmed.is_empty() {
245 return Err(Error::VariableNotFound(path.to_string()));
246 }
247 Ok(trimmed)
248}