use std::fmt;
use serde::Serialize;
use serde::ser;
#[derive(Debug)]
pub(crate) struct NonFinite {
detail: String,
}
impl NonFinite {
fn new(value: f64) -> Self {
Self {
detail: format!("non-finite float {value} has no wire representation"),
}
}
pub(crate) fn detail(&self) -> &str {
&self.detail
}
}
impl fmt::Display for NonFinite {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.detail)
}
}
impl std::error::Error for NonFinite {}
impl ser::Error for NonFinite {
fn custom<T: fmt::Display>(message: T) -> Self {
Self {
detail: message.to_string(),
}
}
}
pub(crate) fn check<T: Serialize + ?Sized>(body: &T) -> Result<(), NonFinite> {
body.serialize(Probe)
}
struct Probe;
struct Compound;
impl ser::Serializer for Probe {
type Ok = ();
type Error = NonFinite;
type SerializeSeq = Compound;
type SerializeTuple = Compound;
type SerializeTupleStruct = Compound;
type SerializeTupleVariant = Compound;
type SerializeMap = Compound;
type SerializeStruct = Compound;
type SerializeStructVariant = Compound;
fn serialize_bool(self, _: bool) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_i8(self, _: i8) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_i16(self, _: i16) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_i32(self, _: i32) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_i64(self, _: i64) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_i128(self, _: i128) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_u8(self, _: u8) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_u16(self, _: u16) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_u32(self, _: u32) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_u64(self, _: u64) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_u128(self, _: u128) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_f32(self, value: f32) -> Result<(), NonFinite> {
if value.is_finite() {
Ok(())
} else {
Err(NonFinite::new(f64::from(value)))
}
}
fn serialize_f64(self, value: f64) -> Result<(), NonFinite> {
if value.is_finite() {
Ok(())
} else {
Err(NonFinite::new(value))
}
}
fn serialize_char(self, _: char) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_str(self, _: &str) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_bytes(self, _: &[u8]) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_none(self) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<(), NonFinite> {
value.serialize(Self)
}
fn serialize_unit(self) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_unit_struct(self, _: &'static str) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
) -> Result<(), NonFinite> {
Ok(())
}
fn serialize_newtype_struct<T: Serialize + ?Sized>(
self,
_: &'static str,
value: &T,
) -> Result<(), NonFinite> {
value.serialize(Self)
}
fn serialize_newtype_variant<T: Serialize + ?Sized>(
self,
_: &'static str,
_: u32,
_: &'static str,
value: &T,
) -> Result<(), NonFinite> {
value.serialize(Self)
}
fn serialize_seq(self, _: Option<usize>) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_tuple(self, _: usize) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_tuple_struct(self, _: &'static str, _: usize) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_map(self, _: Option<usize>) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_struct(self, _: &'static str, _: usize) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn serialize_struct_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Compound, NonFinite> {
Ok(Compound)
}
fn collect_str<T: fmt::Display + ?Sized>(self, _: &T) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeSeq for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeTuple for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeTupleStruct for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeTupleVariant for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeMap for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), NonFinite> {
key.serialize(Probe)
}
fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeStruct for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_field<T: Serialize + ?Sized>(
&mut self,
_: &'static str,
value: &T,
) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
impl ser::SerializeStructVariant for Compound {
type Ok = ();
type Error = NonFinite;
fn serialize_field<T: Serialize + ?Sized>(
&mut self,
_: &'static str,
value: &T,
) -> Result<(), NonFinite> {
value.serialize(Probe)
}
fn end(self) -> Result<(), NonFinite> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::check;
use serde::Serialize;
#[derive(Serialize)]
struct Nested {
values: Vec<f32>,
maybe: Option<f64>,
label: String,
}
#[test]
fn finite_structures_pass() {
let value = Nested {
values: vec![1.5, -2.25],
maybe: Some(0.0),
label: "ok".to_owned(),
};
assert!(check(&value).is_ok());
}
#[test]
fn nan_anywhere_refuses() {
let value = Nested {
values: vec![1.5, f32::NAN],
maybe: None,
label: "bad".to_owned(),
};
let Err(refusal) = check(&value) else {
unreachable!("NaN must refuse")
};
assert!(refusal.detail().contains("non-finite"));
}
#[test]
fn infinity_in_option_refuses() {
let value = Nested {
values: Vec::new(),
maybe: Some(f64::INFINITY),
label: "bad".to_owned(),
};
assert!(check(&value).is_err());
}
}