Validate and clamp deck config when loading

Fixes #2353
This commit is contained in:
Damien Elmes 2023-01-28 15:56:40 +10:00
parent c7e49d8708
commit d20a7d291f
2 changed files with 112 additions and 29 deletions

View File

@ -35,16 +35,10 @@ pub struct DeckConfig {
pub inner: DeckConfigInner, pub inner: DeckConfigInner,
} }
impl Default for DeckConfig { /// NOTE: this does not set the default steps
fn default() -> Self { const DEFAULT_DECK_CONFIG_INNER: DeckConfigInner = DeckConfigInner {
DeckConfig { learn_steps: Vec::new(),
id: DeckConfigId(0), relearn_steps: Vec::new(),
name: "".to_string(),
mtime_secs: Default::default(),
usn: Default::default(),
inner: DeckConfigInner {
learn_steps: vec![1.0, 10.0],
relearn_steps: vec![10.0],
new_per_day: 20, new_per_day: 20,
reviews_per_day: 200, reviews_per_day: 200,
new_per_day_minimum: 0, new_per_day_minimum: 0,
@ -72,7 +66,20 @@ impl Default for DeckConfig {
bury_new: false, bury_new: false,
bury_reviews: false, bury_reviews: false,
bury_interday_learning: false, bury_interday_learning: false,
other: vec![], other: Vec::new(),
};
impl Default for DeckConfig {
fn default() -> Self {
DeckConfig {
id: DeckConfigId(0),
name: "".to_string(),
mtime_secs: Default::default(),
usn: Default::default(),
inner: DeckConfigInner {
learn_steps: vec![1.0, 10.0],
relearn_steps: vec![10.0],
..DEFAULT_DECK_CONFIG_INNER
}, },
} }
} }
@ -178,3 +185,78 @@ impl Collection {
self.remove_deck_config_undoable(conf) self.remove_deck_config_undoable(conf)
} }
} }
impl DeckConfigInner {
/// There was a period of time when the deck options screen was allowing
/// 0/NaN to be persisted, so we need to check the values are within
/// valid bounds when reading from the DB.
pub(crate) fn ensure_values_valid(&mut self) {
let default = DEFAULT_DECK_CONFIG_INNER;
ensure_u32_valid(&mut self.new_per_day, default.new_per_day, 0, 9999);
ensure_u32_valid(&mut self.reviews_per_day, default.reviews_per_day, 0, 9999);
ensure_u32_valid(
&mut self.new_per_day_minimum,
default.new_per_day_minimum,
0,
9999,
);
ensure_f32_valid(&mut self.initial_ease, default.initial_ease, 1.31, 5.0);
ensure_f32_valid(&mut self.easy_multiplier, default.easy_multiplier, 1.0, 5.0);
ensure_f32_valid(&mut self.hard_multiplier, default.hard_multiplier, 0.5, 1.3);
ensure_f32_valid(
&mut self.lapse_multiplier,
default.lapse_multiplier,
0.0,
1.0,
);
ensure_f32_valid(
&mut self.interval_multiplier,
default.interval_multiplier,
0.5,
2.0,
);
ensure_u32_valid(
&mut self.maximum_review_interval,
default.maximum_review_interval,
1,
36_500,
);
ensure_u32_valid(
&mut self.minimum_lapse_interval,
default.minimum_lapse_interval,
1,
36_500,
);
ensure_u32_valid(
&mut self.graduating_interval_good,
default.graduating_interval_good,
1,
36_500,
);
ensure_u32_valid(
&mut self.graduating_interval_easy,
default.graduating_interval_easy,
1,
36_500,
);
ensure_u32_valid(&mut self.leech_threshold, default.leech_threshold, 1, 9999);
ensure_u32_valid(
&mut self.cap_answer_time_to_secs,
default.cap_answer_time_to_secs,
1,
9999,
);
}
}
fn ensure_f32_valid(val: &mut f32, default: f32, min: f32, max: f32) {
if val.is_nan() || *val < min || *val > max {
*val = default;
}
}
fn ensure_u32_valid(val: &mut u32, default: u32, min: u32, max: u32) {
if *val < min || *val > max {
*val = default;
}
}

View File

@ -16,7 +16,8 @@ use crate::deckconfig::DeckConfigInner;
use crate::prelude::*; use crate::prelude::*;
fn row_to_deckconf(row: &Row) -> Result<DeckConfig> { fn row_to_deckconf(row: &Row) -> Result<DeckConfig> {
let config = DeckConfigInner::decode(row.get_ref_unwrap(4).as_blob()?)?; let mut config = DeckConfigInner::decode(row.get_ref_unwrap(4).as_blob()?)?;
config.ensure_values_valid();
Ok(DeckConfig { Ok(DeckConfig {
id: row.get(0)?, id: row.get(0)?,
name: row.get(1)?, name: row.get(1)?,