use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if value.fract() == 0.0
&& value.is_finite()
&& *value >= (i64::MIN as f64)
&& *value <= (i64::MAX as f64)
{
(*value as i64).serialize(serializer)
} else {
value.serialize(serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
f64::deserialize(deserializer)
}
pub mod option {
use super::*;
pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(v) => {
if v.fract() == 0.0
&& v.is_finite()
&& *v >= (i64::MIN as f64)
&& *v <= (i64::MAX as f64)
{
serializer.serialize_some(&(*v as i64))
} else {
serializer.serialize_some(v)
}
}
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
D: Deserializer<'de>,
{
Option::<f64>::deserialize(deserializer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestStruct {
#[serde(with = "super")]
value: f64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestStructOptional {
#[serde(default)]
#[serde(with = "super::option")]
#[serde(skip_serializing_if = "Option::is_none")]
value: Option<f64>,
}
#[test]
fn test_whole_number_no_decimal() {
let test = TestStruct { value: 24000.0 };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":24000}"#);
}
#[test]
fn test_fractional_keeps_decimal() {
let test = TestStruct { value: 3.14 };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":3.14}"#);
}
#[test]
fn test_zero() {
let test = TestStruct { value: 0.0 };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":0}"#);
}
#[test]
fn test_negative_whole() {
let test = TestStruct { value: -100.0 };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":-100}"#);
}
#[test]
fn test_deserialize_from_integer() {
let json = r#"{"value":24000}"#;
let test: TestStruct = serde_json::from_str(json).unwrap();
assert_eq!(test.value, 24000.0);
}
#[test]
fn test_deserialize_from_float() {
let json = r#"{"value":3.14}"#;
let test: TestStruct = serde_json::from_str(json).unwrap();
assert_eq!(test.value, 3.14);
}
#[test]
fn test_roundtrip() {
let original = TestStruct { value: 44100.0 };
let json = serde_json::to_string(&original).unwrap();
let decoded: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn test_optional_some_whole() {
let test = TestStructOptional {
value: Some(16000.0),
};
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":16000}"#);
}
#[test]
fn test_optional_none() {
let test = TestStructOptional { value: None };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{}"#);
}
#[test]
fn test_optional_deserialize_missing() {
let json = r#"{}"#;
let test: TestStructOptional = serde_json::from_str(json).unwrap();
assert_eq!(test.value, None);
}
#[test]
fn test_large_whole_number_outside_i64_range() {
let test = TestStruct { value: 1e20 };
let json = serde_json::to_string(&test).unwrap();
assert_eq!(json, r#"{"value":1e+20}"#);
}
}