use std::{fmt::Debug, fs::OpenOptions, io::Write, path::Path, slice};
use crate::{
codes_message::{BufMessage, CodesMessage},
errors::CodesError,
intermediate_bindings::{
codes_get_message, codes_set_bytes, codes_set_double, codes_set_double_array,
codes_set_long, codes_set_long_array, codes_set_string,
},
};
pub trait KeyWrite<T> {
fn write_key_unchecked(&mut self, name: &str, value: T) -> Result<&mut Self, CodesError>;
}
macro_rules! impl_key_write {
($ec_func:ident, $gen_type:ty) => {
impl KeyWrite<$gen_type> for BufMessage {
fn write_key_unchecked(
&mut self,
name: &str,
value: $gen_type,
) -> Result<&mut Self, CodesError> {
unsafe {
$ec_func(self.message_handle, name, value)?;
}
Ok(self)
}
}
};
}
impl_key_write!(codes_set_long, i64);
impl_key_write!(codes_set_double, f64);
impl_key_write!(codes_set_long_array, &[i64]);
impl_key_write!(codes_set_double_array, &[f64]);
impl_key_write!(codes_set_bytes, &[u8]);
impl_key_write!(codes_set_string, &str);
impl<PA: Debug> CodesMessage<PA> {
pub fn write_to_file<P: AsRef<Path>>(
&self,
file_path: P,
append: bool,
) -> Result<(), CodesError> {
let msg = unsafe { codes_get_message(self.message_handle)? };
let buf = unsafe { slice::from_raw_parts(msg.0.cast(), msg.1) };
let mut file = OpenOptions::new()
.write(true)
.create(true)
.append(append)
.open(file_path)?;
file.write_all(buf)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use anyhow::{Context, Result};
use fallible_iterator::FallibleIterator;
use crate::{
KeyRead,
codes_file::{CodesFile, ProductKind},
codes_message::{DynamicKeyType, KeyWrite},
};
use std::{fs::remove_file, path::Path};
#[test]
fn write_message_ref() -> Result<()> {
let file_path = Path::new("./data/iceland.grib");
let product_kind = ProductKind::GRIB;
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
let out_path = Path::new("./data/iceland_write.grib");
current_message.write_to_file(out_path, false)?;
remove_file(out_path)?;
Ok(())
}
#[test]
fn write_message_clone() -> Result<()> {
let file_path = Path::new("./data/iceland.grib");
let product_kind = ProductKind::GRIB;
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?
.try_clone()?;
drop(handle);
let out_path = Path::new("./data/iceland_write_clone.grib");
current_message.write_to_file(out_path, false)?;
remove_file(out_path)?;
Ok(())
}
#[test]
fn append_message() -> Result<()> {
let product_kind = ProductKind::GRIB;
let out_path = Path::new("./data/iceland_append.grib");
let file_path = Path::new("./data/iceland-surface.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
current_message.write_to_file(out_path, false)?;
let file_path = Path::new("./data/iceland-levels.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
current_message.write_to_file(out_path, true)?;
remove_file(out_path)?;
Ok(())
}
#[test]
fn write_key() -> Result<()> {
let product_kind = ProductKind::GRIB;
let file_path = Path::new("./data/iceland.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
let old_key = current_message.read_key_dynamic("centre")?;
let mut cloned_message = current_message.try_clone()?;
cloned_message.write_key_unchecked("centre", "cnmc")?;
let read_key = cloned_message.read_key_dynamic("centre")?;
assert_ne!(old_key, read_key);
assert_eq!(read_key, DynamicKeyType::Str("cnmc".into()));
Ok(())
}
#[test]
fn write_key_types() -> Result<()> {
let product_kind = ProductKind::GRIB;
let file_path = Path::new("./data/iceland.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let mut message = handle
.ref_message_iter()
.next()?
.context("Message not some")?
.try_clone()?;
let mut values_array: Vec<f64> = message.read_key("values")?;
values_array[0] = 0.0;
message.write_key_unchecked("centre", "cnmc")?; message.write_key_unchecked("day", 3)?; message.write_key_unchecked("latitudeOfFirstGridPointInDegrees", 7.0)?; message.write_key_unchecked("values", values_array.as_slice())?;
Ok(())
}
#[test]
fn edit_keys_and_save() -> Result<()> {
let product_kind = ProductKind::GRIB;
let file_path = Path::new("./data/iceland.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
let old_key = current_message.read_key_dynamic("centre")?;
let mut cloned_message = current_message.try_clone()?;
cloned_message.write_key_unchecked("centre", "cnmc")?;
cloned_message.write_to_file(Path::new("./data/iceland_edit.grib"), false)?;
let file_path = Path::new("./data/iceland_edit.grib");
let mut handle = CodesFile::new_from_file(file_path, product_kind)?;
let current_message = handle
.ref_message_iter()
.next()?
.context("Message not some")?;
let read_key = current_message.read_key_dynamic("centre")?;
assert_ne!(old_key, read_key);
assert_eq!(read_key, DynamicKeyType::Str("cnmc".into()));
remove_file(Path::new("./data/iceland_edit.grib"))?;
Ok(())
}
}