Closes #40556 Release Notes: - settings-ui: Fixed an issue where modifying floating point number fields could result in the values written to the settings file containing IEEE 754 floating point error > [!Note] > Seems like there's another pull request fixing the centered layout in #40661 by normalizing the whole `settings.json` file when updating it. Not sure which is the better way to handle this issue. ## Description This pull request solves the IEEE 754 floating point error when serializing values from settings-ui into `settings.json` by creating a two `serde_helper` to convert the problematic f32 into a formatted two decimal placed f32. Fields currently exists the IEEE 754 error: - Appearance → Unnecessary Code Fade - Editor → Drop Size Target - Window & Layout → Centered Layout Left/Right Padding - Window & Layout → Inactive Opacity ## How to verify ### Unnecessary Code Fade As Is | To Be --- | --- <video src="https://github.com/user-attachments/assets/1bf4bad2-63c5-4b03-ac29-8b6b59569e16" /> | <video src="https://github.com/user-attachments/assets/dadcd4a1-651b-43dd-913f-edae073ceb68" /> ### Drop Size Target As Is | To Be --- | --- <video src="https://github.com/user-attachments/assets/9d5b4173-fcac-44d0-b7fc-772a2e426ef1" /> | <video src="https://github.com/user-attachments/assets/4b5adeaf-e678-494d-bd1b-6c1d55824c43" /> ### Centered Layout Left/Right Padding As Is | To Be --- | --- <video src="https://github.com/user-attachments/assets/33b4e1ff-7ab2-44f7-9e9b-8abad1565d9a" /> | <video src="https://github.com/user-attachments/assets/63d8de9e-28d1-4bd7-a6c9-02452e105486" /> ### Inactive Opacity As Is | To Be --- | --- <video src="https://github.com/user-attachments/assets/a7fe2e72-deb6-41dc-82f3-e2649503b8a4" /> | <video src="https://github.com/user-attachments/assets/993c314f-b6f6-4dcd-8f74-fa357ab063e9" /> --------- Co-authored-by: Ben Kunkle <ben@zed.dev>
136 lines
4.0 KiB
Rust
136 lines
4.0 KiB
Rust
use serde::Serializer;
|
|
|
|
/// Serializes an f32 value with 2 decimal places of precision.
|
|
///
|
|
/// This function rounds the value to 2 decimal places and formats it as a string,
|
|
/// then parses it back to f64 before serialization. This ensures clean JSON output
|
|
/// without IEEE 754 floating-point artifacts.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `value` - The f32 value to serialize
|
|
/// * `serializer` - The serde serializer to use
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Result of the serialization operation
|
|
///
|
|
/// # Usage
|
|
///
|
|
/// This function can be used with Serde's `serialize_with` attribute:
|
|
/// ```
|
|
/// use serde::Serialize;
|
|
/// use settings::serialize_f32_with_two_decimal_places;
|
|
///
|
|
/// #[derive(Serialize)]
|
|
/// struct ExampleStruct(#[serde(serialize_with = "serialize_f32_with_two_decimal_places")] f32);
|
|
/// ```
|
|
pub fn serialize_f32_with_two_decimal_places<S>(
|
|
value: &f32,
|
|
serializer: S,
|
|
) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
let rounded = (value * 100.0).round() / 100.0;
|
|
let formatted = format!("{:.2}", rounded);
|
|
let clean_value: f64 = formatted.parse().unwrap_or(rounded as f64);
|
|
serializer.serialize_f64(clean_value)
|
|
}
|
|
|
|
/// Serializes an optional f32 value with 2 decimal places of precision.
|
|
///
|
|
/// This function handles `Option<f32>` types, serializing `Some` values with 2 decimal
|
|
/// places of precision and `None` values as null. For `Some` values, it rounds to 2 decimal
|
|
/// places and formats as a string, then parses back to f64 before serialization. This ensures
|
|
/// clean JSON output without IEEE 754 floating-point artifacts.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `value` - The optional f32 value to serialize
|
|
/// * `serializer` - The serde serializer to use
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Result of the serialization operation
|
|
///
|
|
/// # Behavior
|
|
///
|
|
/// * `Some(v)` - Serializes the value rounded to 2 decimal places
|
|
/// * `None` - Serializes as JSON null
|
|
///
|
|
/// # Usage
|
|
///
|
|
/// This function can be used with Serde's `serialize_with` attribute:
|
|
/// ```
|
|
/// use serde::Serialize;
|
|
/// use settings::serialize_optional_f32_with_two_decimal_places;
|
|
///
|
|
/// #[derive(Serialize)]
|
|
/// struct ExampleStruct {
|
|
/// #[serde(serialize_with = "serialize_optional_f32_with_two_decimal_places")]
|
|
/// optional_value: Option<f32>,
|
|
/// }
|
|
/// ```
|
|
pub fn serialize_optional_f32_with_two_decimal_places<S>(
|
|
value: &Option<f32>,
|
|
serializer: S,
|
|
) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
match value {
|
|
Some(v) => {
|
|
let rounded = (v * 100.0).round() / 100.0;
|
|
let formatted = format!("{:.2}", rounded);
|
|
let clean_value: f64 = formatted.parse().unwrap_or(rounded as f64);
|
|
serializer.serialize_some(&clean_value)
|
|
}
|
|
None => serializer.serialize_none(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct TestOptional {
|
|
#[serde(serialize_with = "serialize_optional_f32_with_two_decimal_places")]
|
|
value: Option<f32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct TestNonOptional {
|
|
#[serde(serialize_with = "serialize_f32_with_two_decimal_places")]
|
|
value: f32,
|
|
}
|
|
|
|
#[test]
|
|
fn test_serialize_optional_f32_with_two_decimal_places() {
|
|
let cases = [
|
|
(Some(123.456789), r#"{"value":123.46}"#),
|
|
(Some(1.2), r#"{"value":1.2}"#),
|
|
(Some(300.00000), r#"{"value":300.0}"#),
|
|
];
|
|
for (value, expected) in cases {
|
|
let value = TestOptional { value };
|
|
assert_eq!(serde_json::to_string(&value).unwrap(), expected);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_serialize_f32_with_two_decimal_places() {
|
|
let cases = [
|
|
(123.456789, r#"{"value":123.46}"#),
|
|
(1.200, r#"{"value":1.2}"#),
|
|
(300.00000, r#"{"value":300.0}"#),
|
|
];
|
|
for (value, expected) in cases {
|
|
let value = TestNonOptional { value };
|
|
assert_eq!(serde_json::to_string(&value).unwrap(), expected);
|
|
}
|
|
}
|
|
}
|