use serde::Serializer;
pub fn round_2<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if !value.is_finite() {
return serializer.serialize_none();
}
serializer.serialize_f64((value * 100.0).round() / 100.0)
}
pub fn round_4<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if !value.is_finite() {
return serializer.serialize_none();
}
serializer.serialize_f64((value * 10000.0).round() / 10000.0)
}
pub fn round_2_opt<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(v) if v.is_finite() => {
let rounded = (v * 100.0).round() / 100.0;
serializer.serialize_some(&rounded)
}
_ => serializer.serialize_none(),
}
}
pub fn round_4_opt<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(v) if v.is_finite() => {
let rounded = (v * 10000.0).round() / 10000.0;
serializer.serialize_some(&rounded)
}
_ => serializer.serialize_none(),
}
}
pub mod quartiles {
use super::*;
use crate::profile::Quartiles;
pub fn serialize<S>(value: &Option<Quartiles>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(q) => {
use serde::Serialize;
#[derive(Serialize)]
struct RoundedQuartiles {
#[serde(serialize_with = "round_2")]
q1: f64,
#[serde(serialize_with = "round_2")]
q2: f64,
#[serde(serialize_with = "round_2")]
q3: f64,
#[serde(serialize_with = "round_2")]
iqr: f64,
}
let rounded = RoundedQuartiles {
q1: q.q1,
q2: q.q2,
q3: q.q3,
iqr: q.iqr,
};
serializer.serialize_some(&rounded)
}
None => serializer.serialize_none(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::profile::Quartiles;
use serde::Serialize;
use serde_json::json;
#[derive(Serialize)]
struct Round2Value {
#[serde(serialize_with = "round_2")]
value: f64,
}
#[derive(Serialize)]
struct Round4Value {
#[serde(serialize_with = "round_4")]
value: f64,
}
#[derive(Serialize)]
struct Round2OptValue {
#[serde(serialize_with = "round_2_opt")]
value: Option<f64>,
}
#[derive(Serialize)]
struct Round4OptValue {
#[serde(serialize_with = "round_4_opt")]
value: Option<f64>,
}
#[derive(Serialize)]
struct QuartilesValue {
#[serde(serialize_with = "quartiles::serialize")]
value: Option<Quartiles>,
}
#[test]
fn test_round_2_serializes_rounded_value() {
let value = Round2Value { value: 12.345 };
let json = serde_json::to_value(value).unwrap();
assert_eq!(json, json!({ "value": 12.35 }));
}
#[test]
fn test_round_4_serializes_rounded_value() {
let value = Round4Value { value: 12.34567 };
let json = serde_json::to_value(value).unwrap();
assert_eq!(json, json!({ "value": 12.3457 }));
}
#[test]
fn test_non_finite_values_serialize_as_null() {
let round_2_json = serde_json::to_value(Round2Value { value: f64::NAN }).unwrap();
let round_4_json = serde_json::to_value(Round4Value {
value: f64::INFINITY,
})
.unwrap();
assert_eq!(round_2_json, json!({ "value": null }));
assert_eq!(round_4_json, json!({ "value": null }));
}
#[test]
fn test_optional_rounders_handle_some_none_and_non_finite() {
let rounded_2 = serde_json::to_value(Round2OptValue { value: Some(9.876) }).unwrap();
let rounded_4 = serde_json::to_value(Round4OptValue {
value: Some(9.87654),
})
.unwrap();
let none_2 = serde_json::to_value(Round2OptValue { value: None }).unwrap();
let nan_4 = serde_json::to_value(Round4OptValue {
value: Some(f64::NAN),
})
.unwrap();
assert_eq!(rounded_2, json!({ "value": 9.88 }));
assert_eq!(rounded_4, json!({ "value": 9.8765 }));
assert_eq!(none_2, json!({ "value": null }));
assert_eq!(nan_4, json!({ "value": null }));
}
#[test]
fn test_quartiles_serializer_rounds_each_field() {
let value = QuartilesValue {
value: Some(Quartiles {
q1: 1.234,
q2: 2.345,
q3: 3.456,
iqr: 2.222,
}),
};
let json = serde_json::to_value(value).unwrap();
assert_eq!(
json,
json!({
"value": {
"q1": 1.23,
"q2": 2.35,
"q3": 3.46,
"iqr": 2.22
}
})
);
}
#[test]
fn test_quartiles_serializer_handles_none() {
let value = QuartilesValue { value: None };
let json = serde_json::to_value(value).unwrap();
assert_eq!(json, json!({ "value": null }));
}
}