1use std::{cmp::Ordering, fmt::Debug};
2
3use crate::{
4 codes_message::CodesMessage,
5 errors::CodesError,
6 intermediate_bindings::{
7 NativeKeyType, codes_get_bytes, codes_get_double, codes_get_double_array, codes_get_long,
8 codes_get_long_array, codes_get_native_type, codes_get_size, codes_get_string,
9 },
10};
11
12pub trait KeyRead<T> {
14 fn read_key(&self, name: &str) -> Result<T, CodesError>;
44
45 fn read_key_unchecked(&self, name: &str) -> Result<T, CodesError>;
56}
57
58#[doc(hidden)]
59pub trait KeyPropertiesRead {
60 fn get_key_size(&self, key_name: &str) -> Result<usize, CodesError>;
61 fn get_key_native_type(&self, key_name: &str) -> Result<NativeKeyType, CodesError>;
62}
63
64impl<P: Debug> KeyPropertiesRead for CodesMessage<P> {
65 fn get_key_size(&self, key_name: &str) -> Result<usize, CodesError> {
66 unsafe { codes_get_size(self.message_handle, key_name) }
67 }
68
69 fn get_key_native_type(&self, key_name: &str) -> Result<NativeKeyType, CodesError> {
70 unsafe { codes_get_native_type(self.message_handle, key_name) }
71 }
72}
73
74macro_rules! impl_key_read {
75 ($key_sizing:ident, $ec_func:ident, $key_variant:path, $gen_type:ty) => {
76 impl<P: Debug> KeyRead<$gen_type> for CodesMessage<P> {
77 fn read_key_unchecked(&self, key_name: &str) -> Result<$gen_type, CodesError> {
78 unsafe { $ec_func(self.message_handle, key_name) }
79 }
80
81 fn read_key(&self, key_name: &str) -> Result<$gen_type, CodesError> {
82 match self.get_key_native_type(key_name)? {
83 $key_variant => (),
84 _ => return Err(CodesError::WrongRequestedKeyType),
85 }
86
87 let key_size = self.get_key_size(key_name)?;
88
89 key_size_check!($key_sizing, key_size);
90
91 self.read_key_unchecked(key_name)
92 }
93 }
94 };
95}
96
97macro_rules! key_size_check {
98 (scalar, $size_var:ident) => {
100 match $size_var.cmp(&1) {
101 Ordering::Greater => return Err(CodesError::WrongRequestedKeySize),
102 Ordering::Less => return Err(CodesError::IncorrectKeySize),
103 Ordering::Equal => (),
104 }
105 };
106
107 (array, $size_var:ident) => {
108 if $size_var < 1 {
109 return Err(CodesError::IncorrectKeySize);
110 }
111 };
112}
113
114impl_key_read!(scalar, codes_get_long, NativeKeyType::Long, i64);
115impl_key_read!(scalar, codes_get_double, NativeKeyType::Double, f64);
116impl_key_read!(array, codes_get_string, NativeKeyType::Str, String);
117impl_key_read!(array, codes_get_bytes, NativeKeyType::Bytes, Vec<u8>);
118impl_key_read!(array, codes_get_long_array, NativeKeyType::Long, Vec<i64>);
119impl_key_read!(
120 array,
121 codes_get_double_array,
122 NativeKeyType::Double,
123 Vec<f64>
124);
125
126#[derive(Clone, Debug, PartialEq)]
132pub enum DynamicKeyType {
133 #[allow(missing_docs)]
134 Float(f64),
135 #[allow(missing_docs)]
136 Int(i64),
137 #[allow(missing_docs)]
138 FloatArray(Vec<f64>),
139 #[allow(missing_docs)]
140 IntArray(Vec<i64>),
141 #[allow(missing_docs)]
142 Str(String),
143 #[allow(missing_docs)]
144 Bytes(Vec<u8>),
145}
146
147impl<P: Debug> CodesMessage<P> {
148 pub fn read_key_dynamic(&self, key_name: &str) -> Result<DynamicKeyType, CodesError> {
198 let key_type = self.get_key_native_type(key_name)?;
199 let key_size = self.get_key_size(key_name)?;
200
201 match key_type {
202 NativeKeyType::Long => {
203 if key_size == 1 {
204 self.read_key_unchecked(key_name).map(DynamicKeyType::Int)
205 } else if key_size >= 2 {
206 self.read_key_unchecked(key_name)
207 .map(DynamicKeyType::IntArray)
208 } else {
209 return Err(CodesError::IncorrectKeySize);
210 }
211 }
212 NativeKeyType::Double => {
213 if key_size == 1 {
214 self.read_key_unchecked(key_name).map(DynamicKeyType::Float)
215 } else if key_size >= 2 {
216 self.read_key_unchecked(key_name)
217 .map(DynamicKeyType::FloatArray)
218 } else {
219 return Err(CodesError::IncorrectKeySize);
220 }
221 }
222 NativeKeyType::Bytes => self.read_key_unchecked(key_name).map(DynamicKeyType::Bytes),
223 NativeKeyType::Missing => return Err(CodesError::MissingKey),
224 _ => self.read_key_unchecked(key_name).map(DynamicKeyType::Str),
225 }
226 .or_else(|_| self.read_key_unchecked(key_name).map(DynamicKeyType::Bytes))
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use anyhow::{Context, Result};
233
234 use crate::codes_file::{CodesFile, ProductKind};
235 use crate::{CodesError, KeyRead};
236 use crate::{FallibleIterator, codes_message::DynamicKeyType};
237 use std::path::Path;
238
239 #[test]
240 fn key_reader() -> Result<()> {
241 let file_path = Path::new("./data/iceland.grib");
242 let product_kind = ProductKind::GRIB;
243
244 let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
245
246 let current_message = handle
247 .ref_message_iter()
248 .next()?
249 .context("Message not some")?;
250
251 let str_key = current_message.read_key_dynamic("name")?;
252 match str_key {
253 DynamicKeyType::Str(_) => {}
254 _ => panic!("Incorrect variant of string key"),
255 }
256
257 let double_key = current_message.read_key_dynamic("jDirectionIncrementInDegrees")?;
258 match double_key {
259 DynamicKeyType::Float(_) => {}
260 _ => panic!("Incorrect variant of double key"),
261 }
262
263 let long_key = current_message.read_key_dynamic("numberOfPointsAlongAParallel")?;
264 match long_key {
265 DynamicKeyType::Int(_) => {}
266 _ => panic!("Incorrect variant of long key"),
267 }
268
269 let double_arr_key = current_message.read_key_dynamic("values")?;
270 match double_arr_key {
271 DynamicKeyType::FloatArray(_) => {}
272 _ => panic!("Incorrect variant of double array key"),
273 }
274
275 Ok(())
276 }
277
278 #[test]
279 fn era5_keys_dynamic() -> Result<()> {
280 let file_path = Path::new("./data/iceland.grib");
281 let product_kind = ProductKind::GRIB;
282
283 let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
284 let mut current_message = handle
285 .ref_message_iter()
286 .next()?
287 .context("Message not some")?;
288 let key_names = current_message
289 .default_keys_iterator()?
290 .map(|kn| {
292 assert!(!kn.is_empty());
293 Ok(kn)
294 })
295 .collect::<Vec<_>>()?;
296
297 key_names
298 .iter()
299 .for_each(|kn| assert!(current_message.read_key_dynamic(kn).is_ok()));
300
301 Ok(())
302 }
303
304 #[test]
305 fn gfs_keys_dynamic() -> Result<()> {
306 let file_path = Path::new("./data/gfs.grib");
307 let product_kind = ProductKind::GRIB;
308
309 let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
310 let mut current_message = handle
311 .ref_message_iter()
312 .next()?
313 .context("Message not some")?;
314 let key_names = current_message
315 .default_keys_iterator()?
316 .map(|kn| {
318 assert!(!kn.is_empty());
319 Ok(kn)
320 })
321 .collect::<Vec<_>>()?;
322
323 key_names
324 .iter()
325 .for_each(|kn| assert!(current_message.read_key_dynamic(kn).is_ok()));
326
327 Ok(())
328 }
329
330 #[test]
331 fn missing_key() -> Result<()> {
332 let file_path = Path::new("./data/iceland.grib");
333 let product_kind = ProductKind::GRIB;
334
335 let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
336 let current_message = handle
337 .ref_message_iter()
338 .next()?
339 .context("Message not some")?;
340
341 let missing_key = current_message.read_key_dynamic("doesNotExist");
342
343 assert!(missing_key.is_err());
344
345 Ok(())
346 }
347
348 #[test]
349 fn incorrect_key_type() -> Result<()> {
350 let mut handle = CodesFile::new_from_file("./data/iceland.grib", ProductKind::GRIB)?;
351 let current_message = handle
352 .ref_message_iter()
353 .next()?
354 .context("Message not some")?;
355
356 let missing_key: Result<f64, CodesError> = current_message.read_key("shortName");
357
358 assert!(missing_key.is_err());
359
360 Ok(())
361 }
362
363 #[test]
364 fn benchmark_keys() -> Result<()> {
366 let file_path = Path::new("./data/iceland.grib");
367 let product_kind = ProductKind::GRIB;
368
369 let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
370
371 let msg = handle
372 .ref_message_iter()
373 .next()?
374 .context("Message not some")?;
375
376 let _ = msg.read_key_dynamic("dataDate")?;
377 let _ = msg.read_key_dynamic("jDirectionIncrementInDegrees")?;
378 let _ = msg.read_key_dynamic("values")?;
379 let _ = msg.read_key_dynamic("name")?;
380 let _ = msg.read_key_dynamic("section1Padding")?;
381 let _ = msg.read_key_dynamic("experimentVersionNumber")?;
382 let _ = msg
383 .read_key_dynamic("zero")
384 .unwrap_or_else(|_| msg.read_key_dynamic("zeros").unwrap());
385
386 Ok(())
387 }
388}