1use serde::Serializer;
2
3/// Serializes an f32 value with 2 decimal places of precision.
4///
5/// This function rounds the value to 2 decimal places and formats it as a string,
6/// then parses it back to f64 before serialization. This ensures clean JSON output
7/// without IEEE 754 floating-point artifacts.
8///
9/// # Arguments
10///
11/// * `value` - The f32 value to serialize
12/// * `serializer` - The serde serializer to use
13///
14/// # Returns
15///
16/// Result of the serialization operation
17///
18/// # Usage
19///
20/// This function can be used with Serde's `serialize_with` attribute:
21/// ```
22/// use serde::Serialize;
23/// use settings::serialize_f32_with_two_decimal_places;
24///
25/// #[derive(Serialize)]
26/// struct ExampleStruct(#[serde(serialize_with = "serialize_f32_with_two_decimal_places")] f32);
27/// ```
28pub fn serialize_f32_with_two_decimal_places<S>(
29 value: &f32,
30 serializer: S,
31) -> Result<S::Ok, S::Error>
32where
33 S: Serializer,
34{
35 let rounded = (value * 100.0).round() / 100.0;
36 let formatted = format!("{:.2}", rounded);
37 let clean_value: f64 = formatted.parse().unwrap_or(rounded as f64);
38 serializer.serialize_f64(clean_value)
39}
40
41/// Serializes an optional f32 value with 2 decimal places of precision.
42///
43/// This function handles `Option<f32>` types, serializing `Some` values with 2 decimal
44/// places of precision and `None` values as null. For `Some` values, it rounds to 2 decimal
45/// places and formats as a string, then parses back to f64 before serialization. This ensures
46/// clean JSON output without IEEE 754 floating-point artifacts.
47///
48/// # Arguments
49///
50/// * `value` - The optional f32 value to serialize
51/// * `serializer` - The serde serializer to use
52///
53/// # Returns
54///
55/// Result of the serialization operation
56///
57/// # Behavior
58///
59/// * `Some(v)` - Serializes the value rounded to 2 decimal places
60/// * `None` - Serializes as JSON null
61///
62/// # Usage
63///
64/// This function can be used with Serde's `serialize_with` attribute:
65/// ```
66/// use serde::Serialize;
67/// use settings::serialize_optional_f32_with_two_decimal_places;
68///
69/// #[derive(Serialize)]
70/// struct ExampleStruct {
71/// #[serde(serialize_with = "serialize_optional_f32_with_two_decimal_places")]
72/// optional_value: Option<f32>,
73/// }
74/// ```
75pub fn serialize_optional_f32_with_two_decimal_places<S>(
76 value: &Option<f32>,
77 serializer: S,
78) -> Result<S::Ok, S::Error>
79where
80 S: Serializer,
81{
82 match value {
83 Some(v) => {
84 let rounded = (v * 100.0).round() / 100.0;
85 let formatted = format!("{:.2}", rounded);
86 let clean_value: f64 = formatted.parse().unwrap_or(rounded as f64);
87 serializer.serialize_some(&clean_value)
88 }
89 None => serializer.serialize_none(),
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use serde::{Deserialize, Serialize};
97
98 #[derive(Serialize, Deserialize)]
99 struct TestOptional {
100 #[serde(serialize_with = "serialize_optional_f32_with_two_decimal_places")]
101 value: Option<f32>,
102 }
103
104 #[derive(Serialize, Deserialize)]
105 struct TestNonOptional {
106 #[serde(serialize_with = "serialize_f32_with_two_decimal_places")]
107 value: f32,
108 }
109
110 #[test]
111 fn test_serialize_optional_f32_with_two_decimal_places() {
112 let cases = [
113 (Some(123.456789), r#"{"value":123.46}"#),
114 (Some(1.2), r#"{"value":1.2}"#),
115 (Some(300.00000), r#"{"value":300.0}"#),
116 ];
117 for (value, expected) in cases {
118 let value = TestOptional { value };
119 assert_eq!(serde_json::to_string(&value).unwrap(), expected);
120 }
121 }
122
123 #[test]
124 fn test_serialize_f32_with_two_decimal_places() {
125 let cases = [
126 (123.456789, r#"{"value":123.46}"#),
127 (1.200, r#"{"value":1.2}"#),
128 (300.00000, r#"{"value":300.0}"#),
129 ];
130 for (value, expected) in cases {
131 let value = TestNonOptional { value };
132 assert_eq!(serde_json::to_string(&value).unwrap(), expected);
133 }
134 }
135}