use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use super::{FixedP, Precision};
impl<const P: Precision> Serialize for FixedP<P> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&format!("{}", self))
}
}
impl<'de, const P: Precision> Deserialize<'de> for FixedP<P> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct FixedPVisitor<const P: Precision>;
impl<'de, const P: Precision> Visitor<'de> for FixedPVisitor<P> {
type Value = FixedP<P>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(&format!("struct FixedP<{}>", P))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
use core::str::FromStr;
Self::Value::from_str(v).map_err(|e| {
serde::de::Error::custom(&format!(
"unexpected string {}, expected FixedP<{}>: {}",
v, P, e
))
})
}
}
deserializer.deserialize_str(FixedPVisitor)
}
}
#[cfg(test)]
mod test {
use super::{FixedP, Precision};
mod serialize {
use super::*;
#[test]
fn with_frac() -> Result<(), Box<dyn std::error::Error>> {
let s = r#""12.030""#;
let val = FixedP::<3>::from_units_frac(12, 30)?;
let json = serde_json::to_string(&val);
assert!(json.is_ok());
assert_eq!(json.unwrap(), s);
Ok(())
}
#[test]
fn integer_only() -> Result<(), Box<dyn std::error::Error>> {
let s = r#""12.000""#;
let val = FixedP::<3>::from_units(12)?;
let json = serde_json::to_string(&val);
assert!(json.is_ok());
assert_eq!(json.unwrap(), s);
Ok(())
}
}
mod deserialize {
use super::*;
#[test]
fn with_frac() -> Result<(), Box<dyn std::error::Error>> {
let s = r#""12.03""#;
let val = FixedP::<3>::from_units_frac(12, 30)?;
let v = serde_json::from_str::<FixedP<3>>(s);
assert!(v.is_ok());
assert_eq!(v.unwrap(), val);
Ok(())
}
#[test]
fn integer_only() -> Result<(), Box<dyn std::error::Error>> {
let s = r#""12""#;
let val = FixedP::<3>::from_units(12)?;
let v = serde_json::from_str::<FixedP<3>>(s);
assert!(v.is_ok());
assert_eq!(v.unwrap(), val);
Ok(())
}
#[test]
#[allow(clippy::assertions_on_constants)]
fn frac_out_of_range() -> Result<(), Box<dyn std::error::Error>> {
const OVER_1: Precision = 3;
assert!(OVER_1 > 1);
let s = format!(r#""12.{:0width$}""#, 5, width = ((OVER_1 + 1) as usize));
let v = serde_json::from_str::<FixedP<3>>(&s);
assert!(v.is_err());
let expected_error_msg = format!(
"unexpected string {}, expected FixedP<{}>: {}",
s.replace('"', ""),
OVER_1,
crate::Error::<3>::OutOfRangeFrac
);
assert!(format!("{}", v.unwrap_err()).starts_with(&expected_error_msg));
Ok(())
}
#[test]
#[allow(clippy::assertions_on_constants)]
fn integer_out_of_range() -> Result<(), Box<dyn std::error::Error>> {
const OVER_1: Precision = 3;
assert!(OVER_1 > 1);
let s = format!(r#""{}""#, u64::MAX);
let v = serde_json::from_str::<FixedP<3>>(&s);
assert!(v.is_err());
let expected_error_msg = format!(
"unexpected string {}, expected FixedP<{}>: {}",
s.replace('"', ""),
OVER_1,
crate::Error::<OVER_1>::OutOfRangeUnits
);
assert!(format!("{}", v.unwrap_err()).starts_with(&expected_error_msg));
Ok(())
}
#[test]
#[allow(clippy::assertions_on_constants)]
fn negative() -> Result<(), Box<dyn std::error::Error>> {
const OVER_1: Precision = 3;
assert!(OVER_1 > 1);
let s = format!(r#""{}""#, i64::MIN);
let v = serde_json::from_str::<FixedP<3>>(&s);
assert!(v.is_err());
let parse_error = format!("{}", i64::MIN).parse::<FixedP<3>>();
assert!(parse_error.is_err());
let expected_error_msg = format!(
"unexpected string {}, expected FixedP<{}>: {}",
s.replace('"', ""),
OVER_1,
parse_error.unwrap_err()
);
assert!(format!("{}", v.unwrap_err()).starts_with(&expected_error_msg));
Ok(())
}
#[test]
fn non_string() -> Result<(), Box<dyn std::error::Error>> {
let s = "801";
let v = serde_json::from_str::<FixedP<3>>(s);
assert!(v.is_err());
let expected_error_msg = "invalid type: integer `801`, expected struct FixedP<3>";
assert!(format!("{}", v.unwrap_err()).starts_with(expected_error_msg));
Ok(())
}
}
}