Skip to content

Commit

Permalink
preliminary finish docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Quba1 committed Feb 10, 2024
1 parent e145ea4 commit 96207d2
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 259 deletions.
3 changes: 2 additions & 1 deletion src/codes_handle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub struct GribFile {
/// - From GRIB index using [`new_from_index()`](CodesHandle::new_from_index) (with `experimental_index` feature enabled)
///
/// Destructor for this structure does not panic, but some internal functions may rarely fail
/// leading to bugs. Errors encountered during the destructor are logged with [`log`].
/// leading to bugs. Errors encountered in the destructor are logged with [`log`].
///
/// # `FallibleStreamingIterator`
///
Expand Down Expand Up @@ -125,6 +125,7 @@ enum DataContainer {
///Used to indicate to ecCodes how it should decode/encode messages.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ProductKind {
#[allow(missing_docs)]
GRIB = ProductKind_PRODUCT_GRIB as isize,
}

Expand Down
8 changes: 7 additions & 1 deletion src/codes_nearest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ pub struct NearestGridpoint {

impl KeyedMessage {
/// Creates a new instance of [`CodesNearest`] for the `KeyedMessage`.
/// [`CodesNearest`] can be used to find nearest gridpoints for given coordinates in the `KeyedMessage`.
/// [`CodesNearest`] can be used to find nearest gridpoints for given coordinates in the `KeyedMessage`
/// by calling [`find_nearest()`](crate::CodesNearest::find_nearest).
///
/// # Errors
///
/// This function returns [`CodesInternal`](crate::errors::CodesInternal) when
/// internal nearest handle cannot be created.
pub fn codes_nearest(&self) -> Result<CodesNearest, CodesError> {
let nearest_handle = unsafe { codes_grib_nearest_new(self.message_handle)? };

Expand Down
5 changes: 5 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ pub enum CodesError {
#[error("The key is missing in present message")]
MissingKey,

/// Returned when the size of requested key is lower than 1.
/// This indicates corrupted data file, bug in the crate or bug in the ecCodes library.
#[error("Incorrect key size")]
IncorrectKeySize,

/// Returned when codes_handle_clone returns null pointer
/// indicating issues with cloning the message.
#[error("Cannot clone the message")]
Expand Down
114 changes: 81 additions & 33 deletions src/keyed_message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,84 @@ use eccodes_sys::codes_handle;
use log::warn;
use std::ptr::null_mut;

use crate::{intermediate_bindings::{codes_handle_clone, codes_handle_delete}, CodesError};

/// Structure used to access keys inside the GRIB file message.
/// All data (including data values) contained by the file can only be accessed
/// through the message and keys.
///
/// The structure implements `Clone` trait which comes with a memory overhead.
/// You should take care that your system has enough memory before cloning `KeyedMessage`.
///
/// Keys inside the message can be accessed directly with [`read_key()`](KeyedMessage::read_key())
/// function or using [`FallibleIterator`](KeyedMessage#impl-FallibleIterator).
/// The function [`find_nearest()`](crate::codes_nearest::CodesNearest::find_nearest()) allows to get the values of four nearest gridpoints
/// to requested coordinates.
use crate::{
intermediate_bindings::{codes_handle_clone, codes_handle_delete},
CodesError,
};

/// Structure that provides access to the data contained in the GRIB file, which directly corresponds to the message in the GRIB file
///
/// **Usage examples are provided in documentation of each method.**
///
/// 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 [`Key`] in the message has a unique name and a value of type [`KeyType`].
///
/// 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.
///
/// 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.
///
/// Some of the useful keys are: `validityDate`, `validityTime`, `level`, `typeOfLevel`, `shortName`, `units` and `values`.
///
/// Note that names, types and availability of some keys can vary between platforms and ecCodes versions. You should test
/// your code whenever changing the environment.
///
/// [`CodesNearest`](crate::CodesNearest) can be used to find nearest gridpoints for given coordinates in the `KeyedMessage`.
///
/// Most of `KeyedMessage` methods (except for writing) can be used directly with `&KeyedMessage`
/// returned by `CodesHandle` iterator, which provides the best performance.
/// When mutable access or longer liftime is needed the message can be cloned with [`try_clone`](KeyedMessage::try_clone)
/// Note that cloning comes with a performance and memory overhead.
/// You should take care that your system has enough memory before cloning.
///
/// Destructor for this structure does not panic, but some internal functions may rarely fail
/// leading to bugs. Errors encountered in desctructor the are logged with [`log`].
#[derive(Hash, Debug)]
pub struct KeyedMessage {
pub(crate) message_handle: *mut codes_handle,
}

///Structure representing a single key from the `KeyedMessage`.
/// Structure representing a single key in the `KeyedMessage`
#[derive(Clone, Debug, PartialEq)]
pub struct Key {
#[allow(missing_docs)]
pub name: String,
#[allow(missing_docs)]
pub value: KeyType,
}

///Enum to represent and contain all possible types of keys inside `KeyedMessage`.
/// Enum representing the value of [`Key`] from the `KeyedMessage`
///
///Messages inside GRIB files can contain arbitrary keys set by the file author.
///The type of a given key is only known at runtime (after being checked).
///There are several possible types of keys, which are represented by this enum
///and each variant contains the respective data type.
/// 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
/// and each variant contains the respective data type.
#[derive(Clone, Debug, PartialEq)]
pub enum KeyType {
#[allow(missing_docs)]
Float(f64),
#[allow(missing_docs)]
Int(i64),
#[allow(missing_docs)]
FloatArray(Vec<f64>),
#[allow(missing_docs)]
IntArray(Vec<i64>),
#[allow(missing_docs)]
Str(String),
#[allow(missing_docs)]
Bytes(Vec<u8>),
}

impl KeyedMessage {
/// Custom function to clone the `KeyedMessage`. This function comes with memory overhead.
///
///
/// # Errors
/// This function will return [`CodesInternal`](crate::errors::CodesInternal) if ecCodes fails to clone the message.
pub fn try_clone(&self) -> Result<KeyedMessage, CodesError> {
let new_handle =
unsafe { codes_handle_clone(self.message_handle)? };
let new_handle = unsafe { codes_handle_clone(self.message_handle)? };

Ok(KeyedMessage {
message_handle: new_handle,
Expand All @@ -66,19 +95,19 @@ impl KeyedMessage {

#[doc(hidden)]
impl Drop for KeyedMessage {
///Executes the destructor for this type.
///This method calls destructor functions from ecCodes library.
///In some edge cases these functions can return non-zero code.
///In such case all pointers and file descriptors are safely deleted.
///However memory leaks can still occur.
/// Executes the destructor for this type.
/// This method calls destructor functions from ecCodes library.
/// In some edge cases these functions can return non-zero code.
/// In such case all pointers and file descriptors are safely deleted.
/// However memory leaks can still occur.
///
///If any function called in the destructor returns an error warning will appear in log.
///If bugs occur during `CodesHandle` drop please enable log output and post issue on [Github](https://github.com/ScaleWeather/eccodes).
/// If any function called in the destructor returns an error warning will appear in log.
/// If bugs occur during `CodesHandle` drop please enable log output and post issue on [Github](https://github.com/ScaleWeather/eccodes).
///
///Technical note: delete functions in ecCodes can only fail with [`CodesInternalError`](crate::errors::CodesInternal::CodesInternalError)
///when other functions corrupt the inner memory of pointer, in that case memory leak is possible.
///In case of corrupt pointer segmentation fault will occur.
///The pointers are cleared at the end of drop as they are not functional regardless of result of delete functions.
/// Technical note: delete functions in ecCodes can only fail with [`CodesInternalError`](crate::errors::CodesInternal::CodesInternalError)
/// when other functions corrupt the inner memory of pointer, in that case memory leak is possible.
/// In case of corrupt pointer segmentation fault will occur.
/// The pointers are cleared at the end of drop as they are not functional regardless of result of delete functions.
fn drop(&mut self) {
unsafe {
codes_handle_delete(self.message_handle).unwrap_or_else(|error| {
Expand All @@ -98,6 +127,25 @@ mod tests {
use std::path::Path;
use testing_logger;

#[test]
fn check_docs_keys() -> 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 current_message = handle.next()?.context("Message not some")?;

let _ = current_message.read_key("validityDate")?;
let _ = current_message.read_key("validityTime")?;
let _ = current_message.read_key("level")?;
let _ = current_message.read_key("shortName")?;
let _ = current_message.read_key("units")?;
let _ = current_message.read_key("values")?;
let _ = current_message.read_key("typeOfLevel")?;

Ok(())
}

#[test]
fn message_clone_1() -> Result<()> {
let file_path = Path::new("./data/iceland.grib");
Expand Down
99 changes: 48 additions & 51 deletions src/keyed_message/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,54 @@ use crate::{
};

impl KeyedMessage {
///Method to get a [`Key`] with provided name from the `KeyedMessage`.
///
///This function takes a key name and returns the key value as [`Key`]
///if requested key exists. Check the [`Key`] documentation for details
///of possible key types.
///
///## Example
///
///```
/// use eccodes::{ProductKind, CodesHandle, KeyType};
/// # 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;
/// Method to get a [`Key`] with provided name from the `KeyedMessage`, if it exists.
///
/// let mut handle = CodesHandle::new_from_file(file_path, product_kind)?;
/// let message = handle.next()?.context("no message")?;
/// let message_short_name = message.read_key("shortName")?;
/// let expected_short_name = KeyType::Str("msl".to_string());
/// This function check 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.
///
/// assert_eq!(message_short_name.value, expected_short_name);
/// # Ok(())
/// # }
///```
///
///This function will try to retrieve the key of native string type as string even
///when the nul byte is not positioned at the end of key value.
///
///If retrieving the key value in native type fails this function will try to read
///the requested key as bytes.
///
///## Errors
///
///Returns [`CodesInternal::CodesNotFound`](crate::errors::CodesInternal::CodesNotFound)
///wrapped in [`CodesError::Internal`] when a key of given name has not been found in the message.
///
///Returns [`CodesError::MissingKey`] when a given key has a missing type.
///
///Returns [`CodesError::Internal`] when one of internal ecCodes functions to read the key fails.
///
///Returns [`CodesError::CstrUTF8`] and [`CodesError::NulChar`] when the string returned by ecCodes
///library cannot be parsed as valid UTF8 Rust string.
///
///## Panics
///
///Panics 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 panic please check
///if your file is correct and report it on Github.
/// This function will try to retrieve the key of native string type as string even
/// when the nul byte is not positioned at the end of key value.
///
/// If retrieving the key value in native type fails this function will try to read
/// the requested key as bytes.
///
/// # Example
///
/// ```
/// use eccodes::{ProductKind, CodesHandle, KeyType};
/// # 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 message_short_name = message.read_key("shortName")?;
/// let expected_short_name = KeyType::Str("msl".to_string());
///
/// assert_eq!(message_short_name.value, expected_short_name);
/// # Ok(())
/// # }
/// ```
///
/// # Errors
///
/// Returns [`CodesNotFound`](crate::errors::CodesInternal::CodesNotFound)
/// when a key of given name has not been found in the message.
///
/// Returns [`CodesError::MissingKey`] when a given key does not have a specified type.
///
/// Returns [`CodesError::Internal`] when one of internal ecCodes functions to read the key fails.
///
/// Returns [`CodesError::CstrUTF8`] and [`CodesError::NulChar`] when the string returned by ecCodes
/// library cannot be parsed as valid UTF8 Rust string.
///
/// 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(&self, key_name: &str) -> Result<Key, CodesError> {
let key_type;

Expand Down Expand Up @@ -93,7 +90,7 @@ impl KeyedMessage {
Err(err) => Err(err),
}
} else {
panic!("Incorrect key size!");
return Err(CodesError::IncorrectKeySize)
}
}
NativeKeyType::Double => {
Expand Down Expand Up @@ -121,7 +118,7 @@ impl KeyedMessage {
Err(err) => Err(err),
}
} else {
panic!("Incorrect key size!");
return Err(CodesError::IncorrectKeySize)
}
}
NativeKeyType::Bytes => {
Expand Down
Loading

0 comments on commit 96207d2

Please sign in to comment.