//! A serde serializer adapter that writes non-finite floats as 0. //! //! serde_json writes NaN and ±infinity as `null`, which doesn't read back as a number: a single //! NaN anywhere in the model (a rotation, a crop, a stroke width) made a saved file fail to open //! as "damaged". [`Finite`] wraps any `Serialize` value and replaces those floats on the way out; //! everything else passes through unchanged. use serde::ser::{self, Serialize, Serializer}; /// `T`, serialized with non-finite floats written as 0. pub struct Finite<'a, T: ?Sized>(pub &'a T); impl Serialize for Finite<'_, T> { fn serialize(&self, s: S) -> Result { self.0.serialize(Wrap(s)) } } struct Wrap(S); macro_rules! pass { ($($m:ident($t:ty)),* $(,)?) => { $(fn $m(self, v: $t) -> Result { self.0.$m(v) })* }; } impl Serializer for Wrap { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = Wrap; type SerializeTuple = Wrap; type SerializeTupleStruct = Wrap; type SerializeTupleVariant = Wrap; type SerializeMap = Wrap; type SerializeStruct = Wrap; type SerializeStructVariant = Wrap; pass!( serialize_bool(bool), serialize_i8(i8), serialize_i16(i16), serialize_i32(i32), serialize_i64(i64), serialize_i128(i128), serialize_u8(u8), serialize_u16(u16), serialize_u32(u32), serialize_u64(u64), serialize_u128(u128), serialize_char(char), serialize_str(&str), serialize_bytes(&[u8]), ); fn serialize_f32(self, v: f32) -> Result { self.0.serialize_f32(if v.is_finite() { v } else { 0.0 }) } fn serialize_f64(self, v: f64) -> Result { self.0.serialize_f64(if v.is_finite() { v } else { 0.0 }) } fn serialize_none(self) -> Result { self.0.serialize_none() } fn serialize_some(self, v: &T) -> Result { self.0.serialize_some(&Finite(v)) } fn serialize_unit(self) -> Result { self.0.serialize_unit() } fn serialize_unit_struct(self, name: &'static str) -> Result { self.0.serialize_unit_struct(name) } fn serialize_unit_variant(self, name: &'static str, i: u32, variant: &'static str) -> Result { self.0.serialize_unit_variant(name, i, variant) } fn serialize_newtype_struct(self, name: &'static str, v: &T) -> Result { self.0.serialize_newtype_struct(name, &Finite(v)) } fn serialize_newtype_variant(self, name: &'static str, i: u32, variant: &'static str, v: &T) -> Result { self.0.serialize_newtype_variant(name, i, variant, &Finite(v)) } fn serialize_seq(self, len: Option) -> Result { self.0.serialize_seq(len).map(Wrap) } fn serialize_tuple(self, len: usize) -> Result { self.0.serialize_tuple(len).map(Wrap) } fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result { self.0.serialize_tuple_struct(name, len).map(Wrap) } fn serialize_tuple_variant(self, name: &'static str, i: u32, variant: &'static str, len: usize) -> Result { self.0.serialize_tuple_variant(name, i, variant, len).map(Wrap) } fn serialize_map(self, len: Option) -> Result { self.0.serialize_map(len).map(Wrap) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { self.0.serialize_struct(name, len).map(Wrap) } fn serialize_struct_variant( self, name: &'static str, i: u32, variant: &'static str, len: usize, ) -> Result { self.0.serialize_struct_variant(name, i, variant, len).map(Wrap) } fn is_human_readable(&self) -> bool { self.0.is_human_readable() } } macro_rules! compound { ($tr:ident, $m:ident) => { impl ser::$tr for Wrap { type Ok = S::Ok; type Error = S::Error; fn $m(&mut self, v: &T) -> Result<(), S::Error> { self.0.$m(&Finite(v)) } fn end(self) -> Result { self.0.end() } } }; } compound!(SerializeSeq, serialize_element); compound!(SerializeTuple, serialize_element); compound!(SerializeTupleStruct, serialize_field); compound!(SerializeTupleVariant, serialize_field); impl ser::SerializeMap for Wrap { type Ok = S::Ok; type Error = S::Error; fn serialize_key(&mut self, k: &T) -> Result<(), S::Error> { self.0.serialize_key(&Finite(k)) } fn serialize_value(&mut self, v: &T) -> Result<(), S::Error> { self.0.serialize_value(&Finite(v)) } fn end(self) -> Result { self.0.end() } } macro_rules! fields { ($tr:ident) => { impl ser::$tr for Wrap { type Ok = S::Ok; type Error = S::Error; fn serialize_field(&mut self, key: &'static str, v: &T) -> Result<(), S::Error> { self.0.serialize_field(key, &Finite(v)) } fn skip_field(&mut self, key: &'static str) -> Result<(), S::Error> { self.0.skip_field(key) } fn end(self) -> Result { self.0.end() } } }; } fields!(SerializeStruct); fields!(SerializeStructVariant); #[cfg(test)] mod tests { use super::*; #[test] fn non_finite_floats_are_written_as_zero_and_nothing_else_changes() { #[derive(serde::Serialize)] struct T { a: f64, b: Option, c: Vec<(f32, f64)>, d: Option, e: std::collections::BTreeMap, } let t = T { a: f64::NAN, b: Some(f64::INFINITY), c: vec![(f32::NEG_INFINITY, 1.5)], d: None, e: [("k".to_string(), f64::NAN)].into() }; let s = serde_json::to_string(&Finite(&t)).unwrap_or_default(); assert_eq!(s, r#"{"a":0.0,"b":0.0,"c":[[0.0,1.5]],"d":null,"e":{"k":0.0}}"#); } }