Avoid excessive floating point precision when serializing

This commit is contained in:
Damien Elmes 2023-09-23 15:43:11 +10:00
parent 03edb7bf9e
commit 8b849dd629
2 changed files with 38 additions and 15 deletions

View file

@ -5,9 +5,7 @@ use std::collections::HashMap;
use rusqlite::types::FromSql;
use rusqlite::types::FromSqlError;
use rusqlite::types::ToSqlOutput;
use rusqlite::types::ValueRef;
use rusqlite::ToSql;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
@ -78,6 +76,24 @@ impl CardData {
}
None
}
pub(crate) fn convert_to_json(&mut self) -> Result<String> {
if let Some(v) = &mut self.fsrs_stability {
round_to_places(v, 1)
}
if let Some(v) = &mut self.fsrs_difficulty {
round_to_places(v, 3)
}
if let Some(v) = &mut self.fsrs_desired_retention {
round_to_places(v, 2)
}
serde_json::to_string(&self).map_err(Into::into)
}
}
fn round_to_places(value: &mut f32, decimal_places: u32) {
let factor = 10_f32.powi(decimal_places as i32);
*value = (*value * factor).round() / factor;
}
impl FromSql for CardData {
@ -91,17 +107,9 @@ impl FromSql for CardData {
}
}
impl ToSql for CardData {
fn to_sql(&self) -> Result<ToSqlOutput<'_>, rusqlite::Error> {
Ok(ToSqlOutput::Owned(
serde_json::to_string(self).unwrap().into(),
))
}
}
/// Serialize the JSON `data` for a card.
pub(crate) fn card_data_string(card: &Card) -> String {
serde_json::to_string(&CardData::from_card(card)).unwrap()
CardData::from_card(card).convert_to_json().unwrap()
}
fn meta_is_empty(s: &str) -> bool {
@ -143,4 +151,19 @@ mod test {
assert!(validate_custom_data(r#"{"日本語": 5}"#).is_err());
assert!(validate_custom_data(&format!(r#"{{"foo": "{}"}}"#, "x".repeat(100))).is_err());
}
#[test]
fn compact_floats() {
let mut data = CardData {
original_position: None,
fsrs_stability: Some(123.45678),
fsrs_difficulty: Some(1.234567),
fsrs_desired_retention: Some(0.987654),
custom_data: "".to_string(),
};
assert_eq!(
data.convert_to_json().unwrap(),
r#"{"s":123.5,"d":1.235,"dr":0.99}"#
);
}
}

View file

@ -126,7 +126,7 @@ impl super::SqliteStorage {
card.original_due,
card.original_deck_id,
card.flags,
CardData::from_card(card),
CardData::from_card(card).convert_to_json()?,
card.id,
])?;
Ok(())
@ -153,7 +153,7 @@ impl super::SqliteStorage {
card.original_due,
card.original_deck_id,
card.flags,
CardData::from_card(card),
CardData::from_card(card).convert_to_json()?,
])?;
card.id = CardId(self.db.last_insert_rowid());
Ok(())
@ -181,7 +181,7 @@ impl super::SqliteStorage {
card.original_due,
card.original_deck_id,
card.flags,
CardData::from_card(card),
CardData::from_card(card).convert_to_json()?,
])
.map(|n_rows| n_rows == 1)
.map_err(Into::into)
@ -208,7 +208,7 @@ impl super::SqliteStorage {
card.original_due,
card.original_deck_id,
card.flags,
CardData::from_card(card),
CardData::from_card(card).convert_to_json()?,
])?;
Ok(())