diff --git a/rslib/src/card/undo.rs b/rslib/src/card/undo.rs index c0bb0a035..e83f096f7 100644 --- a/rslib/src/card/undo.rs +++ b/rslib/src/card/undo.rs @@ -35,6 +35,14 @@ impl Collection { Ok(()) } + pub(crate) fn add_card_if_unique_undoable(&mut self, card: &Card) -> Result { + let added = self.storage.add_card_if_unique(card)?; + if added { + self.save_undo(UndoableCardChange::Added(Box::new(card.clone()))); + } + Ok(added) + } + pub(super) fn update_card_undoable(&mut self, card: &mut Card, original: Card) -> Result<()> { if card.id.0 == 0 { return Err(AnkiError::invalid_input("card id not set")); diff --git a/rslib/src/import_export/package/apkg/import.rs b/rslib/src/import_export/package/apkg/import.rs index 3956430b1..7acee2cd7 100644 --- a/rslib/src/import_export/package/apkg/import.rs +++ b/rslib/src/import_export/package/apkg/import.rs @@ -41,7 +41,6 @@ struct Context<'a> { remapped_notes: HashMap, remapped_decks: HashMap, remapped_deck_configs: HashMap, - remapped_cards: HashMap, data: ExchangeData, usn: Usn, /// Map of source media files, that do not already exist in the target. @@ -52,6 +51,7 @@ struct Context<'a> { /// Source notes that cannot be imported, because notes with the same guid /// exist in the target, but their notetypes don't match. conflicting_notes: HashSet, + added_cards: HashSet, normalize_notes: bool, } @@ -139,7 +139,7 @@ impl<'a> Context<'a> { remapped_notes: HashMap::new(), remapped_decks: HashMap::new(), remapped_deck_configs: HashMap::new(), - remapped_cards: HashMap::new(), + added_cards: HashSet::new(), used_media_entries: HashMap::new(), normalize_notes, }) @@ -405,9 +405,15 @@ impl<'a> Context<'a> { self.remap_deck_id(&mut card); // TODO: adjust collection-relative due times // TODO: remove cards from filtered decks - let old_id = mem::take(&mut card.id); - self.target_col.add_card(&mut card)?; - self.remapped_cards.insert(old_id, card.id); + self.add_card(&mut card)?; + } + Ok(()) + } + + fn add_card(&mut self, card: &mut Card) -> Result<()> { + card.usn = self.usn; + if self.target_col.add_card_if_unique_undoable(card)? { + self.added_cards.insert(card.id); } Ok(()) } @@ -426,8 +432,7 @@ impl<'a> Context<'a> { fn import_revlog(&mut self) -> Result<()> { for mut entry in mem::take(&mut self.data.revlog) { - if let Some(cid) = self.remapped_cards.get(&entry.cid) { - entry.cid = *cid; + if self.added_cards.contains(&entry.cid) { entry.usn = self.usn; self.target_col.add_revlog_entry_undoable(entry)?; } diff --git a/rslib/src/storage/card/add_card_if_unique.sql b/rslib/src/storage/card/add_card_if_unique.sql new file mode 100644 index 000000000..80b3a6394 --- /dev/null +++ b/rslib/src/storage/card/add_card_if_unique.sql @@ -0,0 +1,41 @@ +INSERT + OR IGNORE INTO cards ( + id, + nid, + did, + ord, + mod, + usn, + type, + queue, + due, + ivl, + factor, + reps, + lapses, + left, + odue, + odid, + flags, + data + ) +VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? + ) \ No newline at end of file diff --git a/rslib/src/storage/card/mod.rs b/rslib/src/storage/card/mod.rs index ac77dd33a..0d3352c42 100644 --- a/rslib/src/storage/card/mod.rs +++ b/rslib/src/storage/card/mod.rs @@ -145,6 +145,34 @@ impl super::SqliteStorage { Ok(()) } + /// Add card if id is unique. True if card was added. + pub(crate) fn add_card_if_unique(&self, card: &Card) -> Result { + self.db + .prepare_cached(include_str!("add_card_if_unique.sql"))? + .execute(params![ + card.id, + card.note_id, + card.deck_id, + card.template_idx, + card.mtime, + card.usn, + card.ctype as u8, + card.queue as i8, + card.due, + card.interval, + card.ease_factor, + card.reps, + card.lapses, + card.remaining_steps, + card.original_due, + card.original_deck_id, + card.flags, + CardData::from_card(card), + ]) + .map(|n_rows| n_rows == 1) + .map_err(Into::into) + } + /// Add or update card, using the provided ID. Used for syncing & undoing. pub(crate) fn add_or_update_card(&self, card: &Card) -> Result<()> { let mut stmt = self.db.prepare_cached(include_str!("add_or_update.sql"))?;