use crate::{CodesError, intermediate_bindings::codes_handle_new_from_file, pointer_guard};
use eccodes_sys::{ProductKind_PRODUCT_GRIB, codes_handle};
use errno::errno;
use libc::{FILE, size_t};
use std::{
fmt::Debug,
fs::{File, OpenOptions},
os::unix::prelude::AsRawFd,
path::Path,
};
use tracing::instrument;
pub use iterator::{ArcMessageIter, RefMessageIter};
mod iterator;
#[derive(Debug)]
pub struct CodesFile<D: Debug> {
pointer: *mut FILE,
product_kind: ProductKind,
_data: D,
}
impl<D: Debug> CodesFile<D> {
fn generate_codes_handle(&mut self) -> Result<*mut codes_handle, CodesError> {
unsafe { codes_handle_new_from_file(self.pointer, self.product_kind) }
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ProductKind {
#[allow(missing_docs)]
GRIB = ProductKind_PRODUCT_GRIB as isize,
}
impl CodesFile<File> {
#[instrument(level = "trace")]
pub fn new_from_file<P: AsRef<Path> + Debug>(
file_path: P,
product_kind: ProductKind,
) -> Result<Self, CodesError> {
let file = OpenOptions::new().read(true).open(file_path)?;
let file_pointer = open_with_fdopen(&file)?;
Ok(Self {
_data: file,
pointer: file_pointer,
product_kind,
})
}
}
impl CodesFile<Vec<u8>> {
#[instrument(level = "trace")]
pub fn new_from_memory(
mut file_data: Vec<u8>,
product_kind: ProductKind,
) -> Result<Self, CodesError> {
let file_pointer = open_with_fmemopen(&mut file_data)?;
Ok(Self {
_data: file_data,
product_kind,
pointer: file_pointer,
})
}
}
#[instrument(level = "trace")]
fn open_with_fdopen(file: &File) -> Result<*mut FILE, CodesError> {
let file_ptr = unsafe { libc::fdopen(file.as_raw_fd(), "r".as_ptr().cast()) };
if file_ptr.is_null() {
let error_val = errno();
let error_code = error_val.0;
return Err(CodesError::LibcNonZero(error_code, error_val));
}
Ok(file_ptr)
}
#[instrument(level = "trace")]
fn open_with_fmemopen(file_data: &mut [u8]) -> Result<*mut FILE, CodesError> {
let file_data_ptr = file_data.as_mut_ptr();
pointer_guard::non_null!(file_data_ptr);
let file_ptr;
unsafe {
file_ptr = libc::fmemopen(
file_data_ptr.cast(),
file_data.len() as size_t,
"r".as_ptr().cast(),
);
}
if file_ptr.is_null() {
let error_val = errno();
let error_code = error_val.0;
return Err(CodesError::LibcNonZero(error_code, error_val));
}
Ok(file_ptr)
}
#[cfg(test)]
mod tests {
use crate::codes_file::{CodesFile, ProductKind};
use anyhow::{Context, Result};
use eccodes_sys::ProductKind_PRODUCT_GRIB;
use fallible_iterator::FallibleIterator;
use std::{fs::File, io::Read, path::Path};
#[test]
fn file_constructor() -> Result<()> {
let file_path = Path::new("./data/iceland.grib");
let product_kind = ProductKind::GRIB;
let codes_file = CodesFile::new_from_file(file_path, product_kind)?;
assert!(!codes_file.pointer.is_null());
assert_eq!(codes_file.product_kind as u32, { ProductKind_PRODUCT_GRIB });
codes_file._data.metadata()?;
Ok(())
}
#[test]
fn memory_constructor() -> Result<()> {
let product_kind = ProductKind::GRIB;
let mut f = File::open(Path::new("./data/iceland.grib"))?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
let codes_file = CodesFile::new_from_memory(buf, product_kind)?;
assert!(!codes_file.pointer.is_null());
assert_eq!(codes_file.product_kind as u32, { ProductKind_PRODUCT_GRIB });
assert!(!codes_file._data.is_empty());
Ok(())
}
#[test]
fn codes_handle_drop_file() -> Result<()> {
let file_path = Path::new("./data/iceland-surface.grib");
let product_kind = ProductKind::GRIB;
let handle = CodesFile::new_from_file(file_path, product_kind)?;
drop(handle);
Ok(())
}
#[test]
fn codes_handle_drop_mem() -> Result<()> {
let product_kind = ProductKind::GRIB;
let mut f = File::open(Path::new("./data/iceland.grib"))?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
let handle = CodesFile::new_from_memory(buf, product_kind)?;
drop(handle);
Ok(())
}
#[test]
fn multiple_drops() -> Result<()> {
{
let file_path = Path::new("./data/iceland-surface.grib");
let product_kind = ProductKind::GRIB;
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let _ref_msg = handle.ref_message_iter().next()?.context("no message")?;
let mut clone_msg = _ref_msg.try_clone()?;
drop(_ref_msg);
let _oth_ref = handle.ref_message_iter().next()?.context("no message")?;
let _nrst = clone_msg.codes_nearest()?;
drop(_nrst);
let _kiter = clone_msg.default_keys_iterator()?;
}
Ok(())
}
}