#![cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
#![deprecated(since = "0.14.0", note = "Feature experimental_index will be removed in 0.14, because it isn't thread-safe. Users will be able to build their own indexes.")]
use crate::{
codes_handle::HandleGenerator,
errors::CodesError,
intermediate_bindings::{
codes_handle_new_from_index, codes_index_add_file, codes_index_delete, codes_index_new,
codes_index_read, codes_index_select_double, codes_index_select_long,
codes_index_select_string,
},
};
use eccodes_sys::{codes_handle, codes_index};
use std::{fmt::Debug, path::Path, ptr::null_mut};
use tracing::instrument;
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
pub struct CodesIndex {
pub(crate) pointer: *mut codes_index,
}
#[cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
pub trait Select<T> {
fn select(self, key: &str, value: T) -> Result<CodesIndex, CodesError>;
}
impl CodesIndex {
#[cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
#[instrument(level = "trace")]
pub fn new_from_keys(keys: &[&str]) -> Result<CodesIndex, CodesError> {
let keys = keys.join(",");
let index_handle;
unsafe {
index_handle = codes_index_new(&keys)?;
}
Ok(CodesIndex {
pointer: index_handle,
})
}
#[cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
#[instrument(level = "trace")]
pub fn read_from_file<P: AsRef<Path> + Debug>(
index_file_path: P,
) -> Result<CodesIndex, CodesError> {
let index_file_path: &Path = index_file_path.as_ref();
let file_path = index_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)?;
}
Ok(CodesIndex {
pointer: index_handle,
})
}
#[cfg_attr(docsrs, doc(cfg(feature = "experimental_index")))]
pub fn add_grib_file<P: AsRef<Path>>(
self,
index_file_path: P,
) -> Result<CodesIndex, CodesError> {
let index_file_path: &Path = index_file_path.as_ref();
let file_path = index_file_path.to_str().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::InvalidData, "Path is not valid utf8")
})?;
let new_index = self;
unsafe {
codes_index_add_file(new_index.pointer, file_path)?;
}
Ok(new_index)
}
}
impl Select<i64> for CodesIndex {
fn select(self, key: &str, value: i64) -> Result<CodesIndex, CodesError> {
let new_index = self;
unsafe {
codes_index_select_long(new_index.pointer, key, value)?;
}
Ok(new_index)
}
}
impl Select<f64> for CodesIndex {
fn select(self, key: &str, value: f64) -> Result<CodesIndex, CodesError> {
let new_index = self;
unsafe {
codes_index_select_double(new_index.pointer, key, value)?;
}
Ok(new_index)
}
}
impl Select<&str> for CodesIndex {
fn select(self, key: &str, value: &str) -> Result<CodesIndex, CodesError> {
let new_index = self;
unsafe {
codes_index_select_string(new_index.pointer, key, value)?;
}
Ok(new_index)
}
}
impl HandleGenerator for CodesIndex {
fn gen_codes_handle(&self) -> Result<*mut codes_handle, CodesError> {
unsafe { codes_handle_new_from_index(self.pointer) }
}
}
#[doc(hidden)]
impl Drop for CodesIndex {
#[instrument(level = "trace")]
fn drop(&mut self) {
unsafe { codes_index_delete(self.pointer) }
self.pointer = null_mut();
}
}
#[cfg(test)]
mod tests {
use anyhow::{Context, Result, bail};
use fallible_iterator::FallibleIterator;
use crate::{
CodesError, CodesHandle,
codes_index::{CodesIndex, Select},
errors::CodesInternal,
};
use std::path::Path;
#[test]
fn index_constructors() -> Result<()> {
{
let keys = ["shortName", "typeOfLevel", "level", "stepType"];
let index = CodesIndex::new_from_keys(&keys)?;
assert!(!index.pointer.is_null());
}
{
let file_path = Path::new("./data/iceland-surface.grib.idx");
let index = CodesIndex::read_from_file(file_path)?;
assert!(!index.pointer.is_null());
}
Ok(())
}
#[test]
fn index_destructor() -> Result<()> {
let keys = vec!["shortName", "typeOfLevel", "level", "stepType"];
let _index = CodesIndex::new_from_keys(&keys)?;
Ok(())
}
#[test]
fn add_file() -> Result<()> {
let keys = vec!["shortName", "typeOfLevel", "level", "stepType"];
let index = CodesIndex::new_from_keys(&keys)?;
let grib_path = Path::new("./data/iceland.grib");
let index = index.add_grib_file(grib_path)?;
assert!(!index.pointer.is_null());
Ok(())
}
#[test]
fn index_selection() -> Result<()> {
let file_path = Path::new("./data/iceland-surface.grib.idx");
let index = CodesIndex::read_from_file(file_path)?
.select("shortName", "2t")?
.select("typeOfLevel", "surface")?
.select("level", 0)?
.select("stepType", "instant")?;
assert!(!index.pointer.is_null());
Ok(())
}
#[test]
fn incorrect_index_path() -> Result<()> {
let file_path = Path::new("./data/iceland-levels-bad-path.grib.idx");
let index = CodesIndex::read_from_file(file_path);
if let Err(CodesError::Internal(err)) = index {
assert_eq!(err, CodesInternal::CodesIoProblem);
} else {
bail!("Expected CodesError::Internal(CodesInternal::CodesIoProblem)");
}
Ok(())
}
#[test]
fn handle_from_index_destructor() -> Result<()> {
let keys = vec!["typeOfLevel", "level"];
let index = CodesIndex::new_from_keys(&keys)?;
let grib_path = Path::new("./data/iceland-levels.grib");
let index = index
.add_grib_file(grib_path)?
.select("typeOfLevel", "isobaricInhPa")?
.select("level", 600)?;
let mut handle = CodesHandle::new_from_index(index)?;
let _ref_msg = handle
.ref_message_generator()
.next()?
.context("no message")?;
Ok(())
}
}