Skip to content

Commit

Permalink
finish docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Quba1 committed Jul 25, 2024
1 parent 220a25b commit 3d9820e
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 69 deletions.
17 changes: 9 additions & 8 deletions src/codes_handle/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand All @@ -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"),
}
Expand All @@ -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"),
}
Expand Down Expand Up @@ -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()?);
}
Expand Down
1 change: 1 addition & 0 deletions src/codes_handle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
115 changes: 100 additions & 15 deletions src/keyed_message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<T> {
/// 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<T, CodesError>;

/// 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<T, CodesError>;
}

/// Provides GRIB key writing capabilites. Implemented by [`KeyedMessage`] for all possible key types.
pub trait KeyWrite<T> {
/// 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
Expand Down
41 changes: 16 additions & 25 deletions src/keyed_message/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<i64> for KeyedMessage {
Expand Down Expand Up @@ -141,9 +141,14 @@ impl KeyRead<Vec<u8>> 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
Expand All @@ -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(())
/// # }
/// ```
Expand All @@ -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<DynamicKey, CodesError> {
pub fn read_key_dynamic(&self, key_name: &str) -> Result<DynamicKeyType, CodesError> {
let key_type;

unsafe {
Expand Down Expand Up @@ -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))
}
}
}
Expand All @@ -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(())
}

Expand Down
6 changes: 2 additions & 4 deletions src/keyed_message/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand All @@ -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"))?;

Expand Down
16 changes: 9 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand All @@ -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
Expand All @@ -80,13 +80,15 @@
//! Messages are represented by the [`KeyedMessage`] structure.
//!
//! [`CodesHandle`] implements [`FallibleStreamingIterator`](CodesHandle#impl-FallibleStreamingIterator-for-CodesHandle<GribFile>)
//! 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).
Expand Down Expand Up @@ -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};
Loading

0 comments on commit 3d9820e

Please sign in to comment.