diff --git a/data/iceland-surface.idx b/data/iceland-surface.idx new file mode 100644 index 0000000..2aae480 Binary files /dev/null and b/data/iceland-surface.idx differ diff --git a/src/codes_handle/keyed_message/mod.rs b/src/codes_handle/keyed_message/mod.rs index 2ca6237..e241674 100644 --- a/src/codes_handle/keyed_message/mod.rs +++ b/src/codes_handle/keyed_message/mod.rs @@ -14,6 +14,7 @@ use crate::{ codes_grib_nearest_new, codes_handle_delete, codes_handle_new_from_message_copy, codes_keys_iterator_delete, }, + CodesHandle, }; use super::{KeysIteratorFlags, NearestGridpoint}; @@ -160,6 +161,20 @@ impl Drop for KeyedMessage { } } +impl TryFrom for KeyedMessage { + type Error = CodesError; + fn try_from(value: CodesHandle) -> Result { + Ok(KeyedMessage { + message_handle: value.file_handle, + iterator_flags: None, + iterator_namespace: None, + keys_iterator: None, + keys_iterator_next_item_exists: false, + nearest_handle: None, + }) + } +} + #[cfg(test)] mod tests { use crate::codes_handle::{CodesHandle, ProductKind}; diff --git a/src/codes_handle/mod.rs b/src/codes_handle/mod.rs index 1c980cc..06d88d0 100644 --- a/src/codes_handle/mod.rs +++ b/src/codes_handle/mod.rs @@ -1,7 +1,9 @@ //!Main crate module containing definition of `CodesHandle` //!and all associated functions and data structures -use crate::errors::CodesError; +use crate::{ + codes_index::CodesIndex, errors::CodesError, intermediate_bindings::codes_handle_new_from_index, +}; use bytes::Bytes; use eccodes_sys::{codes_handle, codes_keys_iterator, codes_nearest, ProductKind_PRODUCT_GRIB}; use errno::errno; @@ -293,22 +295,41 @@ impl Drop for CodesHandle { //use of stream after the call to fclose() is undefined behaviour, so we clear it let return_code; unsafe { - return_code = libc::fclose(self.file_pointer); - } - - if return_code != 0 { - let error_val = errno(); - warn!( + if !self.file_pointer.is_null() { + return_code = libc::fclose(self.file_pointer); + if return_code != 0 { + let error_val = errno(); + warn!( "fclose() returned an error and your file might not have been correctly saved. Error code: {}; Error message: {}", error_val.0, error_val ); + } + } } self.file_pointer = null_mut(); } } +impl TryFrom<&CodesIndex> for CodesHandle { + type Error = CodesError; + fn try_from(value: &CodesIndex) -> Result { + let handle: *mut codes_handle; + unsafe { + handle = codes_handle_new_from_index(value.index_handle)?; + } + + let new_handle = CodesHandle { + _data: DataContainer::FileBytes(Bytes::new()), //unused, index owns data + file_pointer: null_mut(), + file_handle: handle, + product_kind: ProductKind::GRIB, + }; + Ok(new_handle) + } +} + #[cfg(test)] mod tests { use eccodes_sys::ProductKind_PRODUCT_GRIB; diff --git a/src/codes_index/mod.rs b/src/codes_index/mod.rs new file mode 100644 index 0000000..7b3e56f --- /dev/null +++ b/src/codes_index/mod.rs @@ -0,0 +1,118 @@ +//!Main crate module containing definition of `CodesIndex` +//!and all associated functions and data structures + +use crate::{ + errors::CodesError, + intermediate_bindings::{ + codes_index_delete, codes_index_read, codes_index_select_double, codes_index_select_long, + codes_index_select_string, + }, +}; +use eccodes_sys::codes_index; +use log::warn; +use std::path::Path; + +#[derive(Debug)] +pub struct CodesIndex { + pub index_handle: *mut codes_index, +} +pub trait Select { + fn select(&mut self, key: &str, value: T) -> Result<(), CodesError>; +} + +impl CodesIndex { + pub fn new_from_file(file_path: &Path) -> Result { + let file_path_str = file_path.to_str().ok_or_else(|| std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Path is not valid utf8", + ))?; + let index_handle; + unsafe { + index_handle = codes_index_read(file_path_str)?; + } + Ok(CodesIndex { index_handle }) + } +} + +impl Select for CodesIndex { + fn select(&mut self, key: &str, value: i64) -> Result<(), CodesError> { + unsafe { + codes_index_select_long(self.index_handle, key, value)?; + } + Ok(()) + } +} +impl Select for CodesIndex { + fn select(&mut self, key: &str, value: f64) -> Result<(), CodesError> { + unsafe { + codes_index_select_double(self.index_handle, key, value)?; + } + Ok(()) + } +} +impl Select<&str> for CodesIndex { + fn select(&mut self, key: &str, value: &str) -> Result<(), CodesError> { + unsafe { + codes_index_select_string(self.index_handle, key, value)?; + } + Ok(()) + } +} + +impl Drop for CodesIndex { + fn drop(&mut self) { + unsafe { + codes_index_delete(self.index_handle).unwrap_or_else(|error| { + warn!("codes_index_delete() returned an error: {:?}", &error); + }); + } + } +} + +#[cfg(test)] +mod tests { + use crate::KeyType; + use crate::{ + codes_index::{CodesIndex, Select}, + CodesHandle, KeyedMessage, + }; + use std::{borrow::Borrow, path::Path}; + #[test] + fn file_constructor() { + let file_path = Path::new("./data/iceland-surface.idx"); + let index = CodesIndex::new_from_file(file_path).unwrap(); + assert!(!index.index_handle.is_null()); + } + + #[test] + fn grib_handle_from_index_selection() { + let file_path = Path::new("./data/iceland-surface.idx"); + let mut index = CodesIndex::new_from_file(file_path).unwrap(); + index.select("shortName", "2t").unwrap(); + index.select("typeOfLevel", "surface").unwrap(); + index.select("level", 0).unwrap(); + index.select("stepType", "instant").unwrap(); + let mut handle: CodesHandle = index.borrow().try_into().unwrap(); + let current_message: KeyedMessage = handle.try_into().unwrap(); + + let short_name = current_message.read_key("shortName").unwrap(); + match short_name.value { + KeyType::Str(val) => assert!(val == "2t"), + _ => panic!("Unexpected key type"), + }; + let level = current_message.read_key("level").unwrap(); + match level.value { + KeyType::Int(val) => assert!(val == 0), + _ => panic!("Unexpected key type"), + }; + index.select("shortName", "10v").unwrap(); + handle = index.borrow().try_into().unwrap(); + let current_message: KeyedMessage = handle.try_into().unwrap(); + + let short_name = current_message.read_key("shortName").unwrap(); + match short_name.value { + KeyType::Str(val) => assert!(val == "10v"), + _ => panic!("Unexpected key type"), + }; + } +} diff --git a/src/intermediate_bindings.rs b/src/intermediate_bindings.rs index 0397199..609aae9 100644 --- a/src/intermediate_bindings.rs +++ b/src/intermediate_bindings.rs @@ -329,6 +329,85 @@ pub unsafe fn codes_handle_new_from_message_copy(message_buffer: &[u8]) -> *mut ) } +pub unsafe fn codes_index_read(filename: &str) -> Result<*mut codes_index, CodesError> { + let filename = CString::new(filename).unwrap(); + let context: *mut codes_context = ptr::null_mut(); //default context + let mut error_code: i32 = 0; + + let codes_index = eccodes_sys::codes_index_read(context, filename.as_ptr(), &mut error_code); + codes_index.drop_in_place(); + if error_code != 0 { + let err: CodesInternal = FromPrimitive::from_i32(error_code).unwrap(); + return Err(err.into()); + } + Ok(codes_index) +} + +pub unsafe fn codes_index_delete(index: *mut codes_index) -> Result<(), CodesError> { + eccodes_sys::codes_index_delete(index); + Ok(()) +} + +pub unsafe fn codes_index_select_long( + index: *mut codes_index, + key: &str, + value: i64, +) -> Result<(), CodesError> { + let key = CString::new(key).unwrap(); + let error_code = eccodes_sys::codes_index_select_long(index, key.as_ptr(), value); + + if error_code != 0 { + let err: CodesInternal = FromPrimitive::from_i32(error_code).unwrap(); + return Err(err.into()); + } + Ok(()) +} + +pub unsafe fn codes_index_select_double( + index: *mut codes_index, + key: &str, + value: f64, +) -> Result<(), CodesError> { + let key = CString::new(key).unwrap(); + let error_code = eccodes_sys::codes_index_select_double(index, key.as_ptr(), value); + + if error_code != 0 { + let err: CodesInternal = FromPrimitive::from_i32(error_code).unwrap(); + return Err(err.into()); + } + Ok(()) +} + +pub unsafe fn codes_index_select_string( + index: *mut codes_index, + key: &str, + value: &str, +) -> Result<(), CodesError> { + let key = CString::new(key).unwrap(); + let value = CString::new(value).unwrap(); + let error_code = eccodes_sys::codes_index_select_string(index, key.as_ptr(), value.as_ptr()); + + if error_code != 0 { + let err: CodesInternal = FromPrimitive::from_i32(error_code).unwrap(); + return Err(err.into()); + } + Ok(()) +} + +pub unsafe fn codes_handle_new_from_index( + index: *mut codes_index, +) -> Result<*mut codes_handle, CodesError> { + let mut error_code: i32 = 0; + + let codes_handle = eccodes_sys::codes_handle_new_from_index(index, &mut error_code); + + if error_code != 0 { + let err: CodesInternal = FromPrimitive::from_i32(error_code).unwrap(); + return Err(err.into()); + } + Ok(codes_handle) +} + pub unsafe fn codes_keys_iterator_new( handle: *mut codes_handle, flags: u32, diff --git a/src/lib.rs b/src/lib.rs index 0ae82a3..560602f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,6 +217,7 @@ //! pub mod codes_handle; +pub mod codes_index; pub mod errors; mod intermediate_bindings;