From 3d9820ec63843aacf11ecdc13941a0d92f29198c Mon Sep 17 00:00:00 2001 From: Quba1 <22771850+Quba1@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:58:06 +0200 Subject: [PATCH] finish docs --- src/codes_handle/iterator.rs | 17 +++--- src/codes_handle/mod.rs | 1 + src/errors.rs | 2 + src/keyed_message/mod.rs | 115 ++++++++++++++++++++++++++++++----- src/keyed_message/read.rs | 41 +++++-------- src/keyed_message/write.rs | 6 +- src/lib.rs | 16 ++--- src/message_ndarray.rs | 6 +- tests/handle.rs | 10 +-- 9 files changed, 145 insertions(+), 69 deletions(-) diff --git a/src/codes_handle/iterator.rs b/src/codes_handle/iterator.rs index 34068e1..9a30229 100644 --- a/src/codes_handle/iterator.rs +++ b/src/codes_handle/iterator.rs @@ -112,9 +112,9 @@ mod tests { let msg3 = handle.next()?.context("Message not some")?; let key3 = msg3.read_key_dynamic("typeOfLevel")?; - assert_eq!(key1.value, DynamicKeyType::Str("isobaricInhPa".to_string())); - assert_eq!(key2.value, DynamicKeyType::Str("isobaricInhPa".to_string())); - assert_eq!(key3.value, DynamicKeyType::Str("isobaricInhPa".to_string())); + assert_eq!(key1, DynamicKeyType::Str("isobaricInhPa".to_string())); + assert_eq!(key2, DynamicKeyType::Str("isobaricInhPa".to_string())); + assert_eq!(key3, DynamicKeyType::Str("isobaricInhPa".to_string())); Ok(()) } @@ -129,7 +129,7 @@ mod tests { while let Some(msg) = handle.next()? { let key = msg.read_key_dynamic("shortName")?; - match key.value { + match key { DynamicKeyType::Str(_) => {} _ => panic!("Incorrect variant of string key"), } @@ -151,8 +151,8 @@ mod tests { } for msg in handle_collected { - let key = msg.read_key_dynamic("name")?; - match key.value { + let key: DynamicKeyType = msg.read_key_dynamic("name")?; + match key { DynamicKeyType::Str(_) => {} _ => panic!("Incorrect variant of string key"), } @@ -207,8 +207,9 @@ mod tests { let mut level = vec![]; while let Some(msg) = handle.next()? { - if msg.read_key_dynamic("shortName")?.value == DynamicKeyType::Str("msl".to_string()) - && msg.read_key_dynamic("typeOfLevel")?.value == DynamicKeyType::Str("surface".to_string()) + if msg.read_key_dynamic("shortName")? == DynamicKeyType::Str("msl".to_string()) + && msg.read_key_dynamic("typeOfLevel")? + == DynamicKeyType::Str("surface".to_string()) { level.push(msg.try_clone()?); } diff --git a/src/codes_handle/mod.rs b/src/codes_handle/mod.rs index d1480db..a536856 100644 --- a/src/codes_handle/mod.rs +++ b/src/codes_handle/mod.rs @@ -22,6 +22,7 @@ mod iterator; /// This is an internal structure used to access provided file by `CodesHandle`. /// It also allows to differentiate between `CodesHandle` created from file and from index. /// It is not intended to be used directly by the user. +#[doc(hidden)] #[derive(Debug)] pub struct GribFile { pointer: *mut FILE, diff --git a/src/errors.rs b/src/errors.rs index 0410005..4d8ec8f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -48,9 +48,11 @@ pub enum CodesError { #[error("Incorrect key size")] IncorrectKeySize, + /// Returned when trying to read array as number. #[error("Requested key size is incorrect")] WrongRequestedKeySize, + /// Returned when trying to checked read key in non-native type. #[error("Requested key type is incorrect")] WrongRequestedKeyType, diff --git a/src/keyed_message/mod.rs b/src/keyed_message/mod.rs index 8d0ae53..47eb12b 100644 --- a/src/keyed_message/mod.rs +++ b/src/keyed_message/mod.rs @@ -22,12 +22,11 @@ use crate::{ /// /// You can think about the message as a container of data corresponding to a single variable /// at given date, time and level. In ecCodes the message is represented as a collection of unique -/// key-value pairs. Each [`DynamicKey`] in the message has a unique name and a value of type [`DynamicKeyType`]. +/// key-value pairs. /// -/// You can read a `Key` directly using [`read_key()`](KeyedMessage::read_key()) or iterate over -/// all keys using [`KeysIterator`](crate::KeysIterator). You can also modify the message using -/// [`write_key()`](KeyedMessage::write_key()). As of `0.10`, this crate can successfully read all keys -/// from ERA5 and GFS files. +/// You can read a `Key` with static types using [`read_key()`](KeyRead::read_key()) or with [`DynamicKeyType`] using[`read_key_dynamic()`](KeyedMessage::read_key_dynamic()) +/// To iterate over all key names use [`KeysIterator`](crate::KeysIterator). You can also modify the message using +/// [`write_key()`](KeyWrite::write_key()). This crate can successfully read all keys from ERA5 and GFS files. /// /// If you are interested only in getting data values from the message you can use /// [`to_ndarray()`](KeyedMessage::to_ndarray) from the [`message_ndarray`](crate::message_ndarray) module. @@ -52,25 +51,111 @@ pub struct KeyedMessage { pub(crate) message_handle: *mut codes_handle, } +/// Provides GRIB key reading capabilites. Implemented by [`KeyedMessage`] for all possible key types. pub trait KeyRead { + /// Tries to read a key of given name from [`KeyedMessage`]. This function checks if key native type + /// matches the requested type (ie. you cannot read integer as string, or array as a number). + /// + /// # Example + /// + /// ``` + /// # use eccodes::{ProductKind, CodesHandle, KeyRead}; + /// # use std::path::Path; + /// # use anyhow::Context; + /// # use eccodes::FallibleStreamingIterator; + /// # + /// # fn main() -> anyhow::Result<()> { + /// # let file_path = Path::new("./data/iceland.grib"); + /// # let product_kind = ProductKind::GRIB; + /// # + /// let mut handle = CodesHandle::new_from_file(file_path, product_kind)?; + /// let message = handle.next()?.context("no message")?; + /// let short_name: String = message.read_key("shortName")?; + /// + /// assert_eq!(short_name, "msl"); + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// Returns [`WrongRequestedKeySize`](CodesError::WrongRequestedKeyType) when trying to read key in non-native type (use [`unchecked`](KeyRead::read_key_unchecked) instead). + /// + /// Returns [`WrongRequestedKeySize`](CodesError::WrongRequestedKeySize) when trying to read array as integer. + /// + /// Returns [`IncorrectKeySize`](CodesError::IncorrectKeySize) when key size is 0. This can indicate corrupted data. + /// + /// This function will return [`CodesInternal`](crate::errors::CodesInternal) if ecCodes fails to read the key. fn read_key(&self, name: &str) -> Result; + + /// Skips all the checks provided by [`read_key`](KeyRead::read_key) and directly calls ecCodes, ensuring only memory and type safety. + /// + /// This function has better perfomance than [`read_key`](KeyRead::read_key) but all error handling and (possible) + /// type conversions are performed directly by ecCodes. + /// + /// This function is also useful for (not usually used) keys that return incorrect native type. + /// + /// # Example + /// + /// ``` + /// # use eccodes::{ProductKind, CodesHandle, KeyRead}; + /// # use std::path::Path; + /// # use anyhow::Context; + /// # use eccodes::FallibleStreamingIterator; + /// # + /// # fn main() -> anyhow::Result<()> { + /// # let file_path = Path::new("./data/iceland.grib"); + /// # let product_kind = ProductKind::GRIB; + /// # + /// let mut handle = CodesHandle::new_from_file(file_path, product_kind)?; + /// let message = handle.next()?.context("no message")?; + /// let short_name: String = message.read_key_unchecked("shortName")?; + /// + /// assert_eq!(short_name, "msl"); + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// This function will return [`CodesInternal`](crate::errors::CodesInternal) if ecCodes fails to read the key. fn read_key_unchecked(&self, name: &str) -> Result; } +/// Provides GRIB key writing capabilites. Implemented by [`KeyedMessage`] for all possible key types. pub trait KeyWrite { + /// Writes key with given name and value to [`KeyedMessage`] overwriting existing value, unless + /// the key is read-only. This function directly calls ecCodes ensuring only type and memory safety. + /// + /// # Example + /// + /// ``` + /// # use eccodes::{ProductKind, CodesHandle, KeyWrite}; + /// # use std::path::Path; + /// # use anyhow::Context; + /// # use eccodes::FallibleStreamingIterator; + /// # + /// # fn main() -> anyhow::Result<()> { + /// # let file_path = Path::new("./data/iceland.grib"); + /// # let product_kind = ProductKind::GRIB; + /// # + /// let mut handle = CodesHandle::new_from_file(file_path, product_kind)?; + /// + /// // CodesHandle iterator returns immutable messages. + /// // To edit a message it must be cloned. + /// let mut message = handle.next()?.context("no message")?.try_clone()?; + /// message.write_key("level", 1)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// This function will return [`CodesInternal`](crate::errors::CodesInternal) if ecCodes fails to write the key. fn write_key(&mut self, name: &str, value: T) -> Result<(), CodesError>; } -/// Structure representing a single key in the `KeyedMessage` -#[derive(Clone, Debug, PartialEq)] -pub struct DynamicKey { - #[allow(missing_docs)] - pub name: String, - #[allow(missing_docs)] - pub value: DynamicKeyType, -} - -/// Enum of types that value inside [`DynamicKey`] can have +/// Enum of types GRIB key can have. /// /// Messages inside GRIB files can contain keys of arbitrary types, which are known only at runtime (after being checked). /// ecCodes can return several different types of key, which are represented by this enum diff --git a/src/keyed_message/read.rs b/src/keyed_message/read.rs index 1784289..b584237 100644 --- a/src/keyed_message/read.rs +++ b/src/keyed_message/read.rs @@ -7,7 +7,7 @@ use crate::{ codes_get_long_array, codes_get_native_type, codes_get_size, codes_get_string, NativeKeyType, }, - DynamicKey, DynamicKeyType, KeyRead, KeyedMessage, + DynamicKeyType, KeyRead, KeyedMessage, }; impl KeyRead for KeyedMessage { @@ -141,9 +141,14 @@ impl KeyRead> for KeyedMessage { } impl KeyedMessage { - /// Method to get a [`DynamicKey`] with provided name from the `KeyedMessage`, if it exists. + /// Method to get a value of given key with [`DynamicKeyType`] from the `KeyedMessage`, if it exists. /// - /// This function check the type of requested key and tries to read it as the native type. + /// In most cases you should use [`read_key()`](KeyRead::read_key) due to more predictive behaviour + /// and simpler interface. + /// + /// This function exists for backwards compatibility and user convienience. + /// + /// This function checks the type of requested key and tries to read it as the native type. /// That flow adds performance overhead, but makes the function highly unlikely to fail. /// /// This function will try to retrieve the key of native string type as string even @@ -169,7 +174,7 @@ impl KeyedMessage { /// let message_short_name = message.read_key_dynamic("shortName")?; /// let expected_short_name = DynamicKeyType::Str("msl".to_string()); /// - /// assert_eq!(message_short_name.value, expected_short_name); + /// assert_eq!(message_short_name, expected_short_name); /// # Ok(()) /// # } /// ``` @@ -189,7 +194,7 @@ impl KeyedMessage { /// Returns [`CodesError::IncorrectKeySize`] when the size of given key is lower than 1. This indicates corrupted data file, /// bug in the crate or bug in the ecCodes library. If you encounter this error please check /// if your file is correct and report it on Github. - pub fn read_key_dynamic(&self, key_name: &str) -> Result { + pub fn read_key_dynamic(&self, key_name: &str) -> Result { let key_type; unsafe { @@ -279,20 +284,14 @@ impl KeyedMessage { }; if let Ok(value) = key_value { - Ok(DynamicKey { - name: key_name.to_owned(), - value, - }) + Ok(value) } else { let value; unsafe { value = codes_get_bytes(self.message_handle, key_name)?; } - Ok(DynamicKey { - name: key_name.to_owned(), - value: DynamicKeyType::Bytes(value), - }) + Ok(DynamicKeyType::Bytes(value)) } } } @@ -316,39 +315,31 @@ mod tests { let str_key = current_message.read_key_dynamic("name")?; - match str_key.value { + match str_key { DynamicKeyType::Str(_) => {} _ => panic!("Incorrect variant of string key"), } - assert_eq!(str_key.name, "name"); - let double_key = current_message.read_key_dynamic("jDirectionIncrementInDegrees")?; - match double_key.value { + match double_key { DynamicKeyType::Float(_) => {} _ => panic!("Incorrect variant of double key"), } - assert_eq!(double_key.name, "jDirectionIncrementInDegrees"); - let long_key = current_message.read_key_dynamic("numberOfPointsAlongAParallel")?; - match long_key.value { + match long_key { DynamicKeyType::Int(_) => {} _ => panic!("Incorrect variant of long key"), } - assert_eq!(long_key.name, "numberOfPointsAlongAParallel"); - let double_arr_key = current_message.read_key_dynamic("values")?; - match double_arr_key.value { + match double_arr_key { DynamicKeyType::FloatArray(_) => {} _ => panic!("Incorrect variant of double array key"), } - assert_eq!(double_arr_key.name, "values"); - Ok(()) } diff --git a/src/keyed_message/write.rs b/src/keyed_message/write.rs index d15759e..7a68b05 100644 --- a/src/keyed_message/write.rs +++ b/src/keyed_message/write.rs @@ -204,8 +204,7 @@ mod tests { let read_key = current_message.read_key_dynamic("centre")?; assert_ne!(old_key, read_key); - assert_eq!(read_key.name, "centre"); - assert_eq!(read_key.value, DynamicKeyType::Str("cnmc".into())); + assert_eq!(read_key, DynamicKeyType::Str("cnmc".into())); Ok(()) } @@ -232,8 +231,7 @@ mod tests { let read_key = current_message.read_key_dynamic("centre")?; assert_ne!(old_key, read_key); - assert_eq!(read_key.name, "centre"); - assert_eq!(read_key.value, DynamicKeyType::Str("cnmc".into())); + assert_eq!(read_key, DynamicKeyType::Str("cnmc".into())); remove_file(Path::new("./data/iceland_edit.grib"))?; diff --git a/src/lib.rs b/src/lib.rs index e78d97b..c0de849 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ //! Destructors, which cannot panic, report errors through the `log` crate. //! //! None of the functions in this crate explicitly panics. -//! However, users should not that dependencies might panic in some edge cases. +//! However, users should be aware that dependencies might panic in some edge cases. //! //! ## Safety //! @@ -59,7 +59,7 @@ //! This feature is enabled by default. It is currently tested only with simple lat-lon grids. //! //! - `experimental_index` - enables support for creating and using index files for GRIB files. -//! This feature experimental and disabled by default. If you want to use it, please read +//! **This feature is experimental** and disabled by default. If you want to use it, please read //! the information provided in [`codes_index`] documentation. //! //! - `docs` - builds the crate without linking ecCodes, particularly useful when building the documentation @@ -80,13 +80,15 @@ //! Messages are represented by the [`KeyedMessage`] structure. //! //! [`CodesHandle`] implements [`FallibleStreamingIterator`](CodesHandle#impl-FallibleStreamingIterator-for-CodesHandle) -//! which allows you to iterate over messages in the file. The iterator returns `&KeyedMessage` which valid until next iteration. +//! which allows you to iterate over messages in the file. The iterator returns `&KeyedMessage` which valid is until next iteration. //! `KeyedMessage` implements several methods to access the data as needed, most of those can be called directly on `&KeyedMessage`. //! You can also use [`try_clone()`](KeyedMessage::try_clone) to clone the message and prolong its lifetime. //! -//! Data defining and contained by `KeyedMessage` is represented by [`DynamicKey`]s. -//! You can read them directly with [`read_key()`](KeyedMessage::read_key), use [`KeysIterator`](KeyedMessage) -//! to iterate over them or use [`CodesNearest`] to get the values of four nearest gridpoints for given coordinates. +//! Data contained by `KeyedMessage` is represented as *keys* (like in dictionary). +//! Keys can be read with static types using [`read_key()`](KeyedMessage::read_key) or with [dynamic types](`DynamicKeyType`) +//! using [`read_key_dynamic()`](KeyedMessage::read_key_dynamic). To discover what keys are present in a message use [`KeysIterator`](KeyedMessage). +//! +//! You can use [`CodesNearest`] to get the data values of four nearest gridpoints for given coordinates. //! //! You can also modify the message with [`write_key()`](KeyedMessage::write_key) and write //! it to a new file with [`write_to_file()`](KeyedMessage::write_to_file). @@ -227,5 +229,5 @@ pub use codes_nearest::{CodesNearest, NearestGridpoint}; pub use errors::CodesError; pub use fallible_iterator::{FallibleIterator, IntoFallibleIterator}; pub use fallible_streaming_iterator::FallibleStreamingIterator; -pub use keyed_message::{DynamicKey, DynamicKeyType, KeyedMessage, KeyRead, KeyWrite}; +pub use keyed_message::{DynamicKeyType, KeyedMessage, KeyRead, KeyWrite}; pub use keys_iterator::{KeysIterator, KeysIteratorFlags}; diff --git a/src/message_ndarray.rs b/src/message_ndarray.rs index 87fcab5..e60119f 100644 --- a/src/message_ndarray.rs +++ b/src/message_ndarray.rs @@ -1,5 +1,5 @@ #![cfg_attr(docsrs, doc(cfg(feature = "message_ndarray")))] -//! Definition of functions to convert a `KeyedMessage` to ndarray +//! Definitions for converting a `KeyedMessage` to ndarray use ndarray::{s, Array2, Array3}; @@ -155,7 +155,7 @@ mod tests { let mut handle = CodesHandle::new_from_file(file_path, ProductKind::GRIB)?; while let Some(msg) = handle.next()? { - if msg.read_key_dynamic("shortName")?.value == DynamicKeyType::Str("2d".to_string()) { + if msg.read_key_dynamic("shortName")? == DynamicKeyType::Str("2d".to_string()) { let ndarray = msg.to_ndarray()?; // values from xarray @@ -181,7 +181,7 @@ mod tests { let mut handle = CodesHandle::new_from_file(file_path, ProductKind::GRIB)?; while let Some(msg) = handle.next()? { - if msg.read_key_dynamic("shortName")?.value == DynamicKeyType::Str("2d".to_string()) { + if msg.read_key_dynamic("shortName")? == DynamicKeyType::Str("2d".to_string()) { let rmsg = msg.to_lons_lats_values()?; let vals = rmsg.values; diff --git a/tests/handle.rs b/tests/handle.rs index 34f8d5d..2fed63c 100644 --- a/tests/handle.rs +++ b/tests/handle.rs @@ -1,7 +1,7 @@ use std::{path::Path, thread}; use anyhow::{Context, Result}; -use eccodes::{CodesHandle, FallibleStreamingIterator, DynamicKeyType, ProductKind}; +use eccodes::{CodesHandle, DynamicKeyType, FallibleStreamingIterator, ProductKind}; #[test] fn thread_safety() { @@ -22,12 +22,10 @@ fn thread_safety_core() -> Result<()> { let str_key = current_message.read_key_dynamic("name")?; - match str_key.value { + match str_key { DynamicKeyType::Str(_) => {} _ => panic!("Incorrect variant of string key"), } - - assert_eq!(str_key.name, "name"); } drop(handle); @@ -42,13 +40,11 @@ fn thread_safety_core() -> Result<()> { let long_key = current_message.read_key_dynamic("numberOfPointsAlongAParallel")?; - match long_key.value { + match long_key { DynamicKeyType::Int(_) => {} _ => panic!("Incorrect variant of long key"), } - assert_eq!(long_key.name, "numberOfPointsAlongAParallel"); - drop(handle); }