mirror of
https://github.com/ankitects/anki.git
synced 2026-01-15 06:49:03 -05:00
So, this is fun. Apparently "DeckId" is considered preferable to the "DeckID" were were using until now, and the latest clippy will start warning about it. We could of course disable the warning, but probably better to bite the bullet and switch to the naming that's generally considered best.
176 lines
5.6 KiB
Rust
176 lines
5.6 KiB
Rust
// Copyright: Ankitects Pty Ltd and contributors
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
use super::{super::limits::RemainingLimits, DueCard, NewCard, QueueBuilder};
|
|
use crate::{card::CardQueue, prelude::*};
|
|
|
|
impl QueueBuilder {
|
|
/// Assumes cards will arrive sorted in (queue, due) order, so learning
|
|
/// cards come first, and reviews come before day-learning and preview cards.
|
|
pub(in super::super) fn add_due_card(
|
|
&mut self,
|
|
limit: &mut RemainingLimits,
|
|
queue: CardQueue,
|
|
card: DueCard,
|
|
) -> bool {
|
|
let should_add = self.should_add_review_card(card.note_id);
|
|
|
|
match queue {
|
|
CardQueue::Learn | CardQueue::PreviewRepeat => self.learning.push(card),
|
|
CardQueue::DayLearn => {
|
|
self.day_learning.push(card);
|
|
}
|
|
CardQueue::Review => {
|
|
if should_add {
|
|
self.review.push(card);
|
|
limit.review -= 1;
|
|
}
|
|
}
|
|
CardQueue::New
|
|
| CardQueue::Suspended
|
|
| CardQueue::SchedBuried
|
|
| CardQueue::UserBuried => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
limit.review != 0
|
|
}
|
|
|
|
pub(in super::super) fn add_new_card(
|
|
&mut self,
|
|
limit: &mut RemainingLimits,
|
|
card: NewCard,
|
|
) -> bool {
|
|
let already_seen = self.have_seen_note_id(card.note_id);
|
|
if !already_seen {
|
|
self.new.push(card);
|
|
limit.new -= 1;
|
|
return limit.new != 0;
|
|
}
|
|
|
|
// Cards will be arriving in (due, card_id) order, with all
|
|
// siblings sharing the same due number by default. In the
|
|
// common case, card ids will match template order, and nothing
|
|
// special is required. But if some cards have been generated
|
|
// after the initial note creation, they will have higher card
|
|
// ids, and the siblings will thus arrive in the wrong order.
|
|
// Sorting by ordinal in the DB layer is fairly costly, as it
|
|
// doesn't allow us to exit early when the daily limits have
|
|
// been met, so we want to enforce ordering as we add instead.
|
|
let previous_card_was_sibling_with_higher_ordinal = self
|
|
.new
|
|
.last()
|
|
.map(|previous| previous.note_id == card.note_id && previous.extra > card.extra)
|
|
.unwrap_or(false);
|
|
|
|
if previous_card_was_sibling_with_higher_ordinal {
|
|
if self.bury_new {
|
|
// When burying is enabled, we replace the existing sibling
|
|
// with the lower ordinal one.
|
|
*self.new.last_mut().unwrap() = card;
|
|
} else {
|
|
// When burying disabled, we'll want to add this card as well, but
|
|
// not at the end of the list.
|
|
let target_idx = self
|
|
.new
|
|
.iter()
|
|
.enumerate()
|
|
.rev()
|
|
.filter_map(|(idx, queued_card)| {
|
|
if queued_card.note_id != card.note_id || queued_card.extra < card.extra {
|
|
Some(idx + 1)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.next()
|
|
.unwrap_or(0);
|
|
self.new.insert(target_idx, card);
|
|
limit.new -= 1;
|
|
}
|
|
} else {
|
|
// card has arrived in expected order - add if burying disabled
|
|
if !self.bury_new {
|
|
self.new.push(card);
|
|
limit.new -= 1;
|
|
}
|
|
}
|
|
|
|
limit.new != 0
|
|
}
|
|
|
|
fn should_add_review_card(&mut self, note_id: NoteId) -> bool {
|
|
!self.have_seen_note_id(note_id) || !self.bury_reviews
|
|
}
|
|
|
|
/// Mark note seen, and return true if seen before.
|
|
fn have_seen_note_id(&mut self, note_id: NoteId) -> bool {
|
|
!self.seen_note_ids.insert(note_id)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn new_siblings() {
|
|
let mut builder = QueueBuilder {
|
|
bury_new: true,
|
|
..Default::default()
|
|
};
|
|
let mut limits = RemainingLimits {
|
|
review: 0,
|
|
new: 100,
|
|
};
|
|
|
|
let cards = vec![
|
|
NewCard {
|
|
id: CardId(1),
|
|
note_id: NoteId(1),
|
|
extra: 0,
|
|
..Default::default()
|
|
},
|
|
NewCard {
|
|
id: CardId(2),
|
|
note_id: NoteId(2),
|
|
extra: 1,
|
|
..Default::default()
|
|
},
|
|
NewCard {
|
|
id: CardId(3),
|
|
note_id: NoteId(2),
|
|
extra: 2,
|
|
..Default::default()
|
|
},
|
|
NewCard {
|
|
id: CardId(4),
|
|
note_id: NoteId(2),
|
|
extra: 0,
|
|
..Default::default()
|
|
},
|
|
];
|
|
|
|
for card in &cards {
|
|
builder.add_new_card(&mut limits, card.clone());
|
|
}
|
|
|
|
assert_eq!(builder.new[0].id, CardId(1));
|
|
assert_eq!(builder.new[1].id, CardId(4));
|
|
assert_eq!(builder.new.len(), 2);
|
|
|
|
// with burying disabled, we should get all siblings in order
|
|
builder.bury_new = false;
|
|
builder.new.truncate(0);
|
|
|
|
for card in &cards {
|
|
builder.add_new_card(&mut limits, card.clone());
|
|
}
|
|
|
|
assert_eq!(builder.new[0].id, CardId(1));
|
|
assert_eq!(builder.new[1].id, CardId(4));
|
|
assert_eq!(builder.new[2].id, CardId(2));
|
|
assert_eq!(builder.new[3].id, CardId(3));
|
|
}
|
|
}
|