From e5b7ed6cad95fb079f235e410979defddfde1a3a Mon Sep 17 00:00:00 2001 From: Daniel Pechersky Date: Sun, 14 Sep 2025 11:33:59 +0700 Subject: [PATCH] Simplify update_memory_state --- rslib/src/scheduler/fsrs/memory_state.rs | 167 ++++++++++++----------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index 420ead5a3..7eb0c8fbb 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -80,11 +80,7 @@ impl Collection { SearchBuilder::all([search.into(), SearchNode::State(StateKind::New).negated()]); let revlog = self.revlog_for_srs(search)?; let reschedule = req.as_ref().map(|e| e.reschedule).unwrap_or_default(); - let last_revlog_info = if reschedule { - Some(get_last_revlog_info(&revlog)) - } else { - None - }; + let last_revlog_info = reschedule.then(|| get_last_revlog_info(&revlog)); let mut rescheduler = self .get_config_bool(BoolKey::LoadBalancerEnabled) .then(|| Rescheduler::new(self)) @@ -101,12 +97,26 @@ impl Collection { )?; let mut progress = self.new_progress_handler::(); progress.update(false, |s| s.total_cards = items.len() as u32)?; + + let Some(req) = &req else { + // clear FSRS data if FSRS is disabled + for (idx, (card_id, _)) in items.into_iter().enumerate() { + progress.update(true, |state| state.current_cards = idx as u32 + 1)?; + let mut card = self.storage.get_card(card_id)?.or_not_found(card_id)?; + let original = card.clone(); + card.clear_fsrs_data(); + self.update_card_inner(&mut card, original, usn)?; + } + return Ok(()); + }; + let preset_desired_retention = req.preset_desired_retention; + for (idx, (card_id, item)) in items.into_iter().enumerate() { progress.update(true, |state| state.current_cards = idx as u32 + 1)?; let mut card = self.storage.get_card(card_id)?.or_not_found(card_id)?; let original = card.clone(); - if let Some(req) = &req { - let preset_desired_retention = req.preset_desired_retention; + + 'update_card: { // Store decay and desired retention in the card so that add-ons, card info, // stats and browser search/sorts don't need to access the deck config. // Unlike memory states, scheduler doesn't use decay and dr stored in the card. @@ -117,85 +127,78 @@ impl Collection { .unwrap_or(&preset_desired_retention); card.desired_retention = Some(desired_retention); card.decay = decay; - if let Some(item) = item { - card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?; - // if rescheduling - if let Some(reviews) = &last_revlog_info { - // and we have a last review time for the card - if let Some(last_info) = reviews.get(&card.id) { - if let Some(last_review) = &last_info.last_reviewed_at { - let days_elapsed = - timing.next_day_at.elapsed_days_since(*last_review) as i32; - // and the card's not new - if let Some(state) = &card.memory_state { - // or in (re)learning - if card.ctype == CardType::Review { - let deck = self - .get_deck(card.original_or_current_deck_id())? - .or_not_found(card.original_or_current_deck_id())?; - let deckconfig_id = deck.config_id().unwrap(); - // reschedule it - let original_interval = card.interval; - let interval = fsrs.next_interval( - Some(state.stability), - desired_retention, - 0, - ); - card.interval = rescheduler - .as_mut() - .and_then(|r| { - r.find_interval( - interval, - 1, - req.max_interval, - days_elapsed as u32, - deckconfig_id, - get_fuzz_seed(&card, true), - ) - }) - .unwrap_or_else(|| { - with_review_fuzz( - card.get_fuzz_factor(true), - interval, - 1, - req.max_interval, - ) - }); - let due = if card.original_due != 0 { - &mut card.original_due - } else { - &mut card.due - }; - let new_due = (timing.days_elapsed as i32) - - days_elapsed - + card.interval as i32; - if let Some(rescheduler) = &mut rescheduler { - rescheduler.update_due_cnt_per_day( - *due, - new_due, - deckconfig_id, - ); - } - *due = new_due; - // Add a rescheduled revlog entry - self.log_rescheduled_review( - &card, - original_interval, - usn, - )?; - } - } - } - } - } - } else { + let Some(item) = item else { // clear memory states if item is None card.memory_state = None; + break 'update_card; + }; + card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?; + + // if rescheduling + let Some(reviews) = &last_revlog_info else { + break 'update_card; + }; + + // and we have a last review time for the card + let Some(last_info) = reviews.get(&card.id) else { + break 'update_card; + }; + let Some(last_review) = &last_info.last_reviewed_at else { + break 'update_card; + }; + + // and the card's not new + let Some(state) = &card.memory_state else { + break 'update_card; + }; + // or in (re)learning + if card.ctype != CardType::Review { + break 'update_card; + }; + + let deck = self + .get_deck(card.original_or_current_deck_id())? + .or_not_found(card.original_or_current_deck_id())?; + let deckconfig_id = deck.config_id().unwrap(); + // reschedule it + let days_elapsed = timing.next_day_at.elapsed_days_since(*last_review) as i32; + let original_interval = card.interval; + let interval = fsrs.next_interval(Some(state.stability), desired_retention, 0); + card.interval = rescheduler + .as_mut() + .and_then(|r| { + r.find_interval( + interval, + 1, + req.max_interval, + days_elapsed as u32, + deckconfig_id, + get_fuzz_seed(&card, true), + ) + }) + .unwrap_or_else(|| { + with_review_fuzz( + card.get_fuzz_factor(true), + interval, + 1, + req.max_interval, + ) + }); + let due = if card.original_due != 0 { + &mut card.original_due + } else { + &mut card.due + }; + let new_due = + (timing.days_elapsed as i32) - days_elapsed + card.interval as i32; + if let Some(rescheduler) = &mut rescheduler { + rescheduler.update_due_cnt_per_day(*due, new_due, deckconfig_id); } - } else { - // clear FSRS data if FSRS is disabled - card.clear_fsrs_data(); + *due = new_due; + // Add a rescheduled revlog entry + self.log_rescheduled_review(&card, original_interval, usn)?; } + self.update_card_inner(&mut card, original, usn)?; } }