dataprof_core/
serde_helpers.rs1use serde::Serializer;
4
5pub fn round_2<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
8where
9 S: Serializer,
10{
11 if !value.is_finite() {
12 return serializer.serialize_none();
13 }
14 serializer.serialize_f64((value * 100.0).round() / 100.0)
15}
16
17pub fn round_4<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
20where
21 S: Serializer,
22{
23 if !value.is_finite() {
24 return serializer.serialize_none();
25 }
26 serializer.serialize_f64((value * 10000.0).round() / 10000.0)
27}
28
29pub fn round_2_opt<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
32where
33 S: Serializer,
34{
35 match value {
36 Some(v) if v.is_finite() => {
37 let rounded = (v * 100.0).round() / 100.0;
38 serializer.serialize_some(&rounded)
39 }
40 _ => serializer.serialize_none(),
41 }
42}
43
44pub fn round_4_opt<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
47where
48 S: Serializer,
49{
50 match value {
51 Some(v) if v.is_finite() => {
52 let rounded = (v * 10000.0).round() / 10000.0;
53 serializer.serialize_some(&rounded)
54 }
55 _ => serializer.serialize_none(),
56 }
57}
58
59pub mod quartiles {
61 use super::*;
62 use crate::profile::Quartiles;
63
64 pub fn serialize<S>(value: &Option<Quartiles>, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 match value {
69 Some(q) => {
70 use serde::Serialize;
71
72 #[derive(Serialize)]
73 struct RoundedQuartiles {
74 #[serde(serialize_with = "round_2")]
75 q1: f64,
76 #[serde(serialize_with = "round_2")]
77 q2: f64,
78 #[serde(serialize_with = "round_2")]
79 q3: f64,
80 #[serde(serialize_with = "round_2")]
81 iqr: f64,
82 }
83
84 let rounded = RoundedQuartiles {
85 q1: q.q1,
86 q2: q.q2,
87 q3: q.q3,
88 iqr: q.iqr,
89 };
90 serializer.serialize_some(&rounded)
91 }
92 None => serializer.serialize_none(),
93 }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::profile::Quartiles;
101 use serde::Serialize;
102 use serde_json::json;
103
104 #[derive(Serialize)]
105 struct Round2Value {
106 #[serde(serialize_with = "round_2")]
107 value: f64,
108 }
109
110 #[derive(Serialize)]
111 struct Round4Value {
112 #[serde(serialize_with = "round_4")]
113 value: f64,
114 }
115
116 #[derive(Serialize)]
117 struct Round2OptValue {
118 #[serde(serialize_with = "round_2_opt")]
119 value: Option<f64>,
120 }
121
122 #[derive(Serialize)]
123 struct Round4OptValue {
124 #[serde(serialize_with = "round_4_opt")]
125 value: Option<f64>,
126 }
127
128 #[derive(Serialize)]
129 struct QuartilesValue {
130 #[serde(serialize_with = "quartiles::serialize")]
131 value: Option<Quartiles>,
132 }
133
134 #[test]
135 fn test_round_2_serializes_rounded_value() {
136 let value = Round2Value { value: 12.345 };
137 let json = serde_json::to_value(value).unwrap();
138 assert_eq!(json, json!({ "value": 12.35 }));
139 }
140
141 #[test]
142 fn test_round_4_serializes_rounded_value() {
143 let value = Round4Value { value: 12.34567 };
144 let json = serde_json::to_value(value).unwrap();
145 assert_eq!(json, json!({ "value": 12.3457 }));
146 }
147
148 #[test]
149 fn test_non_finite_values_serialize_as_null() {
150 let round_2_json = serde_json::to_value(Round2Value { value: f64::NAN }).unwrap();
151 let round_4_json = serde_json::to_value(Round4Value {
152 value: f64::INFINITY,
153 })
154 .unwrap();
155
156 assert_eq!(round_2_json, json!({ "value": null }));
157 assert_eq!(round_4_json, json!({ "value": null }));
158 }
159
160 #[test]
161 fn test_optional_rounders_handle_some_none_and_non_finite() {
162 let rounded_2 = serde_json::to_value(Round2OptValue { value: Some(9.876) }).unwrap();
163 let rounded_4 = serde_json::to_value(Round4OptValue {
164 value: Some(9.87654),
165 })
166 .unwrap();
167 let none_2 = serde_json::to_value(Round2OptValue { value: None }).unwrap();
168 let nan_4 = serde_json::to_value(Round4OptValue {
169 value: Some(f64::NAN),
170 })
171 .unwrap();
172
173 assert_eq!(rounded_2, json!({ "value": 9.88 }));
174 assert_eq!(rounded_4, json!({ "value": 9.8765 }));
175 assert_eq!(none_2, json!({ "value": null }));
176 assert_eq!(nan_4, json!({ "value": null }));
177 }
178
179 #[test]
180 fn test_quartiles_serializer_rounds_each_field() {
181 let value = QuartilesValue {
182 value: Some(Quartiles {
183 q1: 1.234,
184 q2: 2.345,
185 q3: 3.456,
186 iqr: 2.222,
187 }),
188 };
189 let json = serde_json::to_value(value).unwrap();
190
191 assert_eq!(
192 json,
193 json!({
194 "value": {
195 "q1": 1.23,
196 "q2": 2.35,
197 "q3": 3.46,
198 "iqr": 2.22
199 }
200 })
201 );
202 }
203
204 #[test]
205 fn test_quartiles_serializer_handles_none() {
206 let value = QuartilesValue { value: None };
207 let json = serde_json::to_value(value).unwrap();
208 assert_eq!(json, json!({ "value": null }));
209 }
210}