Ok, this is what I came up with:
use serde::ser::{self, Serialize, SerializeSeq, Serializer};
use std::{
any::{Any, TypeId},
io,
};
#[derive(Debug, PartialEq, Clone, Copy)]
enum FormatVar {
Default,
Float(usize),
LowerExp(usize),
UpperExp(usize),
}
impl Default for FormatVar {
fn default() -> Self {
FormatVar::Default
}
}
#[derive(Debug, PartialEq, Clone, Copy, Default)]
struct DataVar<'a, T>
where
T: Any + Serialize,
{
name: &'a str,
data: &'a [T],
format: FormatVar,
}
impl<'a, T> DataVar<'a, T>
where
T: Any + Serialize,
{
pub fn new(name: &'a str, data: &'a [T], format: FormatVar) -> Self {
Self { name, data, format }
}
}
impl<'a, T> Serialize for DataVar<'a, T>
where
T: Any + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
macro_rules! serialize_fmt {
($fmt:expr, $n:expr, $($ty:ident),*) => {
{
let mut seq = serializer.serialize_seq(Some(self.data.len()))?;
$(
if TypeId::of::<T>() == TypeId::of::<$ty>() {
for e in self.data {
seq.serialize_element(&format_args!(
$fmt,
$n,
*(e as &dyn Any).downcast_ref::<$ty>().unwrap() as f64
))?;
}
} else
)*
{
return Err(ser::Error::custom(format!("format \"{:?}\" not supported for variable \"{}\" data type", self.format, self.name)))
}
seq.end()
}
};
}
match self.format {
FormatVar::Default => self.data.serialize(serializer),
FormatVar::Float(n) => serialize_fmt!(
"{:.*}", n, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64
),
FormatVar::LowerExp(n) => serialize_fmt!(
"{:.*e}", n, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64
),
FormatVar::UpperExp(n) => serialize_fmt!(
"{:.*E}", n, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64
),
}
}
}
It's quite cumbersome but it works. Do you think it could be improved? Another simpler option (instead of using Any
and downcast_ref
) could be to call for each sequence element first the serialize
method and then parse::<f64>
, but I excluded it because I guess it would be slower, what do you think?
A last issue that I need to fix is that formatted numbers are serialized as strings, this is a problem for instance with JSON format which retains type information (not for CSV).
Since serializing formatted numbers could be of general use, I suggested to add this capability on Serde's repository:
https://github.com/serde-rs/serde/issues/1462