From 7550e6241cd1baa78e61be5039ae274c5ad47522 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 23 May 2020 15:09:16 +1000 Subject: [PATCH] migrate decks and dconf methods --- proto/backend.proto | 81 +++++--- pylib/anki/decks.py | 31 +-- pylib/anki/rsbackend.py | 256 +++++++++++------------ rslib/src/backend/mod.rs | 427 ++++++++++++++++++++------------------- 4 files changed, 415 insertions(+), 380 deletions(-) diff --git a/proto/backend.proto b/proto/backend.proto index 4251d5ff3..cc8c4b88c 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -33,28 +33,63 @@ message Bytes { bytes val = 1; } +message Bool { + bool val = 1; +} + // New style RPC definitions /////////////////////////////////////////////////////////// service BackendService { - rpc RenderExistingCard (RenderExistingCardIn) returns (RenderCardOut); - rpc RenderUncommittedCard (RenderUncommittedCardIn) returns (RenderCardOut); - rpc SchedTimingToday (Empty) returns (SchedTimingTodayOut); - rpc DeckTree (DeckTreeIn) returns (DeckTreeNode); - rpc SearchCards (SearchCardsIn) returns (SearchCardsOut); - rpc SearchNotes (SearchNotesIn) returns (SearchNotesOut); - rpc CheckMedia (Empty) returns (CheckMediaOut); - rpc LocalMinutesWest (Int64) returns (Int32); - rpc StripAVTags (String) returns (String); + // card rendering + rpc ExtractAVTags (ExtractAVTagsIn) returns (ExtractAVTagsOut); rpc ExtractLatex (ExtractLatexIn) returns (ExtractLatexOut); - rpc DeckTreeLegacy (Empty) returns (Bytes); - rpc CheckDatabase (Empty) returns (CheckDatabaseOut); rpc GetEmptyCards (Empty) returns (EmptyCardsReport); + rpc RenderExistingCard (RenderExistingCardIn) returns (RenderCardOut); + rpc RenderUncommittedCard (RenderUncommittedCardIn) returns (RenderCardOut); + rpc StripAVTags (String) returns (String); + + // searching + + rpc SearchCards (SearchCardsIn) returns (SearchCardsOut); + rpc SearchNotes (SearchNotesIn) returns (SearchNotesOut); + + // scheduling + + rpc LocalMinutesWest (Int64) returns (Int32); + rpc SchedTimingToday (Empty) returns (SchedTimingTodayOut); + + // media + + rpc CheckMedia (Empty) returns (CheckMediaOut); rpc SyncMedia (SyncMediaIn) returns (Empty); rpc TrashMediaFiles (TrashMediaFilesIn) returns (Empty); - rpc GetDeckLegacy (Int64) returns (Bytes); + + // decks + + rpc AddOrUpdateDeckLegacy (AddOrUpdateDeckLegacyIn) returns (Int64); + rpc DeckTree (DeckTreeIn) returns (DeckTreeNode); + rpc DeckTreeLegacy (Empty) returns (Bytes); + rpc GetAllDecksLegacy (Empty) returns (Bytes); rpc GetDeckIDByName (String) returns (Int64); + rpc GetDeckLegacy (Int64) returns (Bytes); + rpc GetDeckNames (GetDeckNamesIn) returns (DeckNames); + rpc NewDeckLegacy (Bool) returns (Bytes); + rpc RemoveDeck (Int64) returns (Empty); + + // deck config + + rpc AddOrUpdateDeckConfigLegacy (AddOrUpdateDeckConfigLegacyIn) returns (Int64); + rpc AllDeckConfigLegacy (Empty) returns (Bytes); + rpc GetDeckConfigLegacy (Int64) returns (Bytes); + rpc NewDeckConfigLegacy (Empty) returns (Bytes); + rpc RemoveDeckConfig (Int64) returns (Empty); + + // misc + + rpc CheckDatabase (Empty) returns (CheckDatabaseOut); + } // Protobuf stored in .anki2 files @@ -312,11 +347,6 @@ message BackendInput { int64 get_card = 38; Card update_card = 39; Card add_card = 40; - int64 get_deck_config = 41; - AddOrUpdateDeckConfigIn add_or_update_deck_config = 42; - Empty all_deck_config = 43; - Empty new_deck_config = 44; - int64 remove_deck_config = 45; Empty abort_media_sync = 46; Empty before_upload = 47; RegisterTagsIn register_tags = 48; @@ -328,7 +358,6 @@ message BackendInput { Empty get_all_config = 55; int32 get_changed_notetypes = 56; AddOrUpdateNotetypeIn add_or_update_notetype = 57; - Empty get_all_decks = 58; StockNoteType get_stock_notetype_legacy = 60; int64 get_notetype_legacy = 61; Empty get_notetype_names = 62; @@ -339,10 +368,6 @@ message BackendInput { AddNoteIn add_note = 67; Note update_note = 68; int64 get_note = 69; - GetDeckNamesIn get_deck_names = 73; - AddOrUpdateDeckLegacyIn add_or_update_deck_legacy = 74; - bool new_deck_legacy = 75; - int64 remove_deck = 76; FieldNamesForNotesIn field_names_for_notes = 78; FindAndReplaceIn find_and_replace = 79; AfterNoteUpdatesIn after_note_updates = 80; @@ -374,11 +399,6 @@ message BackendOutput { GetCardOut get_card = 38; Empty update_card = 39; int64 add_card = 40; - bytes get_deck_config = 41; - int64 add_or_update_deck_config = 42; - bytes all_deck_config = 43; - bytes new_deck_config = 44; - Empty remove_deck_config = 45; Empty before_upload = 47; bool register_tags = 48; AllTagsOut all_tags = 50; @@ -389,7 +409,6 @@ message BackendOutput { bytes get_all_config = 55; bytes get_changed_notetypes = 56; int64 add_or_update_notetype = 57; - bytes get_all_decks = 58; bytes get_notetype_legacy = 61; NoteTypeNames get_notetype_names = 62; NoteTypeUseCounts get_notetype_names_and_counts = 63; @@ -399,10 +418,6 @@ message BackendOutput { int64 add_note = 67; Empty update_note = 68; Note get_note = 69; - DeckNames get_deck_names = 73; - int64 add_or_update_deck_legacy = 74; - bytes new_deck_legacy = 75; - Empty remove_deck = 76; FieldNamesForNotesOut field_names_for_notes = 78; uint32 find_and_replace = 79; Empty after_note_updates = 80; @@ -692,7 +707,7 @@ message CloseCollectionIn { bool downgrade_to_schema11 = 1; } -message AddOrUpdateDeckConfigIn { +message AddOrUpdateDeckConfigLegacyIn { bytes config = 1; bool preserve_usn_and_mtime = 2; } diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 99ca29787..c5e3c1bf1 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -11,7 +11,7 @@ import anki.backend_pb2 as pb from anki.consts import * from anki.errors import DeckRenameError from anki.lang import _ -from anki.rsbackend import DeckTreeNode, NotFoundError, from_json_bytes +from anki.rsbackend import DeckTreeNode, NotFoundError, from_json_bytes, to_json_bytes from anki.utils import ids2str, intTime # legacy code may pass this in as the type argument to .id() @@ -108,8 +108,8 @@ class DeckManager: self, skip_empty_default=False, include_filtered=True ) -> Sequence[pb.DeckNameID]: "A sorted sequence of deck names and IDs." - return self.col.backend.get_deck_names_and_ids( - skip_empty_default, include_filtered + return self.col.backend.get_deck_names( + skip_empty_default=skip_empty_default, include_filtered=include_filtered ) def id_for_name(self, name: str) -> Optional[int]: @@ -125,10 +125,10 @@ class DeckManager: return not self.get_legacy(int(id)) def get_all_legacy(self) -> List[Dict]: - return list(self.col.backend.get_all_decks().values()) + return list(from_json_bytes(self.col.backend.get_all_decks_legacy()).values()) def new_deck_legacy(self, filtered: bool) -> Dict: - return self.col.backend.new_deck_legacy(filtered) + return from_json_bytes(self.col.backend.new_deck_legacy(filtered)) def deck_tree(self) -> pb.DeckTreeNode: return self.col.backend.deck_tree(include_counts=False, top_deck_id=0) @@ -201,11 +201,11 @@ class DeckManager: def update(self, g: Dict[str, Any], preserve_usn=True) -> None: "Add or update an existing deck. Used for syncing and merging." try: - self.col.backend.add_or_update_deck_legacy(g, preserve_usn) + g["id"] = self.col.backend.add_or_update_deck_legacy( + to_json_bytes(g), preserve_usn + ) except anki.rsbackend.DeckIsFilteredError: raise DeckRenameError("deck was filtered") - except anki.rsbackend.ExistsError: - raise DeckRenameError("deck already exists") def rename(self, g: Dict[str, Any], newName: str) -> None: "Rename deck prefix to NAME if not exists. Updates children." @@ -257,7 +257,7 @@ class DeckManager: def all_config(self) -> List: "A list of all deck config." - return list(self.col.backend.all_deck_config()) + return list(from_json_bytes(self.col.backend.all_deck_config_legacy())) def confForDid(self, did: int) -> Any: deck = self.get(did, default=False) @@ -274,10 +274,15 @@ class DeckManager: return deck def get_config(self, conf_id: int) -> Any: - return self.col.backend.get_deck_config(conf_id) + try: + return from_json_bytes(self.col.backend.get_deck_config_legacy(conf_id)) + except NotFoundError: + return None def update_config(self, conf: Dict[str, Any], preserve_usn=False) -> None: - self.col.backend.add_or_update_deck_config(conf, preserve_usn) + conf["id"] = self.col.backend.add_or_update_deck_config_legacy( + to_json_bytes(conf), preserve_usn + ) def add_config( self, name: str, clone_from: Optional[Dict[str, Any]] = None @@ -286,7 +291,7 @@ class DeckManager: conf = copy.deepcopy(clone_from) conf["id"] = 0 else: - conf = self.col.backend.new_deck_config() + conf = from_json_bytes(self.col.backend.new_deck_config_legacy()) conf["name"] = name self.update_config(conf) return conf @@ -321,7 +326,7 @@ class DeckManager: def restoreToDefault(self, conf) -> None: oldOrder = conf["new"]["order"] - new = self.col.backend.new_deck_config() + new = from_json_bytes(self.col.backend.new_deck_config_legacy()) new["id"] = conf["id"] new["name"] = conf["name"] self.update_config(new) diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index b0b78ea0c..409302ad2 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -320,36 +320,6 @@ class RustBackend: def add_card(self, card: BackendCard) -> int: return self._run_command(pb.BackendInput(add_card=card)).add_card - def get_deck_config(self, dcid: int) -> Dict[str, Any]: - jstr = self._run_command(pb.BackendInput(get_deck_config=dcid)).get_deck_config - return orjson.loads(jstr) - - def add_or_update_deck_config(self, conf: Dict[str, Any], preserve_usn) -> None: - conf_json = orjson.dumps(conf) - id = self._run_command( - pb.BackendInput( - add_or_update_deck_config=pb.AddOrUpdateDeckConfigIn( - config=conf_json, preserve_usn_and_mtime=preserve_usn - ) - ) - ).add_or_update_deck_config - conf["id"] = id - - def all_deck_config(self) -> Sequence[Dict[str, Any]]: - jstr = self._run_command( - pb.BackendInput(all_deck_config=pb.Empty()) - ).all_deck_config - return orjson.loads(jstr) - - def new_deck_config(self) -> Dict[str, Any]: - jstr = self._run_command( - pb.BackendInput(new_deck_config=pb.Empty()) - ).new_deck_config - return orjson.loads(jstr) - - def remove_deck_config(self, dcid: int) -> None: - self._run_command(pb.BackendInput(remove_deck_config=dcid)) - def abort_media_sync(self): self._run_command(pb.BackendInput(abort_media_sync=pb.Empty())) @@ -420,12 +390,6 @@ class RustBackend: ).get_changed_notetypes return orjson.loads(jstr) - def get_all_decks(self) -> Dict[str, Dict[str, Any]]: - jstr = self._run_command( - pb.BackendInput(get_all_decks=pb.Empty()) - ).get_all_decks - return orjson.loads(jstr) - def get_stock_notetype_legacy(self, kind: StockNoteType) -> Dict[str, Any]: bytes = self._run_command( pb.BackendInput(get_stock_notetype_legacy=kind) @@ -495,40 +459,6 @@ class RustBackend: except NotFoundError: return None - def get_deck_names_and_ids( - self, skip_empty_default: bool, include_filtered: bool = True - ) -> Sequence[pb.DeckNameID]: - return self._run_command( - pb.BackendInput( - get_deck_names=pb.GetDeckNamesIn( - skip_empty_default=skip_empty_default, - include_filtered=include_filtered, - ) - ) - ).get_deck_names.entries - - def add_or_update_deck_legacy( - self, deck: Dict[str, Any], preserve_usn: bool - ) -> None: - deck_json = orjson.dumps(deck) - id = self._run_command( - pb.BackendInput( - add_or_update_deck_legacy=pb.AddOrUpdateDeckLegacyIn( - deck=deck_json, preserve_usn_and_mtime=preserve_usn - ) - ) - ).add_or_update_deck_legacy - deck["id"] = id - - def new_deck_legacy(self, filtered: bool) -> Dict[str, Any]: - jstr = self._run_command( - pb.BackendInput(new_deck_legacy=filtered) - ).new_deck_legacy - return orjson.loads(jstr) - - def remove_deck(self, did: int) -> None: - self._run_command(pb.BackendInput(remove_deck=did)) - def field_names_for_note_ids(self, nids: List[int]) -> Sequence[str]: return self._run_command( pb.BackendInput(field_names_for_notes=pb.FieldNamesForNotesIn(nids=nids)), @@ -621,10 +551,30 @@ class RustBackend: # @@AUTOGEN@@ + def extract_av_tags(self, text: str, question_side: bool) -> pb.ExtractAVTagsOut: + input = pb.ExtractAVTagsIn(text=text, question_side=question_side) + output = pb.ExtractAVTagsOut() + output.ParseFromString(self._run_command2(1, input)) + return output + + def extract_latex( + self, text: str, svg: bool, expand_clozes: bool + ) -> pb.ExtractLatexOut: + input = pb.ExtractLatexIn(text=text, svg=svg, expand_clozes=expand_clozes) + output = pb.ExtractLatexOut() + output.ParseFromString(self._run_command2(2, input)) + return output + + def get_empty_cards(self) -> pb.EmptyCardsReport: + input = pb.Empty() + output = pb.EmptyCardsReport() + output.ParseFromString(self._run_command2(3, input)) + return output + def render_existing_card(self, card_id: int, browser: bool) -> pb.RenderCardOut: input = pb.RenderExistingCardIn(card_id=card_id, browser=browser) output = pb.RenderCardOut() - output.ParseFromString(self._run_command2(1, input)) + output.ParseFromString(self._run_command2(4, input)) return output def render_uncommitted_card( @@ -634,97 +584,81 @@ class RustBackend: note=note, card_ord=card_ord, template=template, fill_empty=fill_empty ) output = pb.RenderCardOut() - output.ParseFromString(self._run_command2(2, input)) + output.ParseFromString(self._run_command2(5, input)) return output - def sched_timing_today(self) -> pb.SchedTimingTodayOut: - input = pb.Empty() - output = pb.SchedTimingTodayOut() - output.ParseFromString(self._run_command2(3, input)) - return output - - def deck_tree(self, include_counts: bool, top_deck_id: int) -> pb.DeckTreeNode: - input = pb.DeckTreeIn(include_counts=include_counts, top_deck_id=top_deck_id) - output = pb.DeckTreeNode() - output.ParseFromString(self._run_command2(4, input)) - return output + def strip_av_tags(self, val: str) -> str: + input = pb.String(val=val) + output = pb.String() + output.ParseFromString(self._run_command2(6, input)) + return output.val def search_cards(self, search: str, order: pb.SortOrder) -> Sequence[int]: input = pb.SearchCardsIn(search=search, order=order) output = pb.SearchCardsOut() - output.ParseFromString(self._run_command2(5, input)) + output.ParseFromString(self._run_command2(7, input)) return output.card_ids def search_notes(self, search: str) -> Sequence[int]: input = pb.SearchNotesIn(search=search) output = pb.SearchNotesOut() - output.ParseFromString(self._run_command2(6, input)) + output.ParseFromString(self._run_command2(8, input)) return output.note_ids - def check_media(self) -> pb.CheckMediaOut: - input = pb.Empty() - output = pb.CheckMediaOut() - output.ParseFromString(self._run_command2(7, input)) - return output - def local_minutes_west(self, val: int) -> int: input = pb.Int64(val=val) output = pb.Int32() - output.ParseFromString(self._run_command2(8, input)) - return output.val - - def strip_av_tags(self, val: str) -> str: - input = pb.String(val=val) - output = pb.String() output.ParseFromString(self._run_command2(9, input)) return output.val - def extract_av_tags(self, text: str, question_side: bool) -> pb.ExtractAVTagsOut: - input = pb.ExtractAVTagsIn(text=text, question_side=question_side) - output = pb.ExtractAVTagsOut() + def sched_timing_today(self) -> pb.SchedTimingTodayOut: + input = pb.Empty() + output = pb.SchedTimingTodayOut() output.ParseFromString(self._run_command2(10, input)) return output - def extract_latex( - self, text: str, svg: bool, expand_clozes: bool - ) -> pb.ExtractLatexOut: - input = pb.ExtractLatexIn(text=text, svg=svg, expand_clozes=expand_clozes) - output = pb.ExtractLatexOut() + def check_media(self) -> pb.CheckMediaOut: + input = pb.Empty() + output = pb.CheckMediaOut() output.ParseFromString(self._run_command2(11, input)) return output - def deck_tree_legacy(self) -> bytes: - input = pb.Empty() - output = pb.Bytes() - output.ParseFromString(self._run_command2(12, input)) - return output.val - - def check_database(self) -> Sequence[str]: - input = pb.Empty() - output = pb.CheckDatabaseOut() - output.ParseFromString(self._run_command2(13, input)) - return output.problems - - def get_empty_cards(self) -> pb.EmptyCardsReport: - input = pb.Empty() - output = pb.EmptyCardsReport() - output.ParseFromString(self._run_command2(14, input)) - return output - def sync_media(self, hkey: str, endpoint: str) -> pb.Empty: input = pb.SyncMediaIn(hkey=hkey, endpoint=endpoint) output = pb.Empty() - output.ParseFromString(self._run_command2(15, input)) + output.ParseFromString(self._run_command2(12, input)) return output def trash_media_files(self, fnames: Sequence[str]) -> pb.Empty: input = pb.TrashMediaFilesIn(fnames=fnames) output = pb.Empty() - output.ParseFromString(self._run_command2(16, input)) + output.ParseFromString(self._run_command2(13, input)) return output - def get_deck_legacy(self, val: int) -> bytes: - input = pb.Int64(val=val) + def add_or_update_deck_legacy( + self, deck: bytes, preserve_usn_and_mtime: bool + ) -> int: + input = pb.AddOrUpdateDeckLegacyIn( + deck=deck, preserve_usn_and_mtime=preserve_usn_and_mtime + ) + output = pb.Int64() + output.ParseFromString(self._run_command2(14, input)) + return output.val + + def deck_tree(self, include_counts: bool, top_deck_id: int) -> pb.DeckTreeNode: + input = pb.DeckTreeIn(include_counts=include_counts, top_deck_id=top_deck_id) + output = pb.DeckTreeNode() + output.ParseFromString(self._run_command2(15, input)) + return output + + def deck_tree_legacy(self) -> bytes: + input = pb.Empty() + output = pb.Bytes() + output.ParseFromString(self._run_command2(16, input)) + return output.val + + def get_all_decks_legacy(self) -> bytes: + input = pb.Empty() output = pb.Bytes() output.ParseFromString(self._run_command2(17, input)) return output.val @@ -735,6 +669,74 @@ class RustBackend: output.ParseFromString(self._run_command2(18, input)) return output.val + def get_deck_legacy(self, val: int) -> bytes: + input = pb.Int64(val=val) + output = pb.Bytes() + output.ParseFromString(self._run_command2(19, input)) + return output.val + + def get_deck_names( + self, skip_empty_default: bool, include_filtered: bool + ) -> Sequence[pb.DeckNameID]: + input = pb.GetDeckNamesIn( + skip_empty_default=skip_empty_default, include_filtered=include_filtered + ) + output = pb.DeckNames() + output.ParseFromString(self._run_command2(20, input)) + return output.entries + + def new_deck_legacy(self, val: bool) -> bytes: + input = pb.Bool(val=val) + output = pb.Bytes() + output.ParseFromString(self._run_command2(21, input)) + return output.val + + def remove_deck(self, val: int) -> pb.Empty: + input = pb.Int64(val=val) + output = pb.Empty() + output.ParseFromString(self._run_command2(22, input)) + return output + + def add_or_update_deck_config_legacy( + self, config: bytes, preserve_usn_and_mtime: bool + ) -> int: + input = pb.AddOrUpdateDeckConfigLegacyIn( + config=config, preserve_usn_and_mtime=preserve_usn_and_mtime + ) + output = pb.Int64() + output.ParseFromString(self._run_command2(23, input)) + return output.val + + def all_deck_config_legacy(self) -> bytes: + input = pb.Empty() + output = pb.Bytes() + output.ParseFromString(self._run_command2(24, input)) + return output.val + + def get_deck_config_legacy(self, val: int) -> bytes: + input = pb.Int64(val=val) + output = pb.Bytes() + output.ParseFromString(self._run_command2(25, input)) + return output.val + + def new_deck_config_legacy(self) -> bytes: + input = pb.Empty() + output = pb.Bytes() + output.ParseFromString(self._run_command2(26, input)) + return output.val + + def remove_deck_config(self, val: int) -> pb.Empty: + input = pb.Int64(val=val) + output = pb.Empty() + output.ParseFromString(self._run_command2(27, input)) + return output + + def check_database(self) -> Sequence[str]: + input = pb.Empty() + output = pb.CheckDatabaseOut() + output.ParseFromString(self._run_command2(28, input)) + return output.problems + # @@AUTOGEN@@ diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 0d667847c..3544be70a 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -7,7 +7,8 @@ use crate::{ backend_proto as pb, backend_proto::builtin_search_order::BuiltinSortKind, backend_proto::{ - AddOrUpdateDeckConfigIn, BackendResult, Empty, RenderedTemplateReplacement, SyncMediaIn, + AddOrUpdateDeckConfigLegacyIn, BackendResult, Empty, RenderedTemplateReplacement, + SyncMediaIn, }, card::{Card, CardID}, card::{CardQueue, CardType}, @@ -172,6 +173,8 @@ impl From<()> for pb::Empty { } impl BackendService for Backend { + // card rendering + fn render_existing_card( &mut self, input: pb::RenderExistingCardIn, @@ -200,80 +203,26 @@ impl BackendService for Backend { }) } - fn sched_timing_today(&mut self, _input: pb::Empty) -> Result { - self.with_col(|col| col.timing_today().map(Into::into)) - } - - fn deck_tree(&mut self, input: pb::DeckTreeIn) -> Result { - let lim = if input.top_deck_id > 0 { - Some(DeckID(input.top_deck_id)) - } else { - None - }; - self.with_col(|col| col.deck_tree(input.include_counts, lim)) - } - - fn check_media(&mut self, _input: pb::Empty) -> Result { - let callback = - |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); - + fn get_empty_cards(&mut self, _input: pb::Empty) -> Result { self.with_col(|col| { - let mgr = MediaManager::new(&col.media_folder, &col.media_db)?; - col.transact(None, |ctx| { - let mut checker = MediaChecker::new(ctx, &mgr, callback); - let mut output = checker.check()?; + let mut empty = col.empty_cards()?; + let report = col.empty_cards_report(&mut empty)?; - let report = checker.summarize_output(&mut output); - - Ok(pb::CheckMediaOut { - unused: output.unused, - missing: output.missing, - report, - have_trash: output.trash_count > 0, - }) + let mut outnotes = vec![]; + for (_ntid, notes) in empty { + outnotes.extend(notes.into_iter().map(|e| pb::NoteWithEmptyCards { + note_id: e.nid.0, + will_delete_note: e.empty.len() == e.current_count, + card_ids: e.empty.into_iter().map(|(_ord, id)| id.0).collect(), + })) + } + Ok(pb::EmptyCardsReport { + report, + notes: outnotes, }) }) } - fn search_cards(&mut self, input: pb::SearchCardsIn) -> Result { - self.with_col(|col| { - let order = if let Some(order) = input.order { - use pb::sort_order::Value as V; - match order.value { - Some(V::None(_)) => SortMode::NoOrder, - Some(V::Custom(s)) => SortMode::Custom(s), - Some(V::FromConfig(_)) => SortMode::FromConfig, - Some(V::Builtin(b)) => SortMode::Builtin { - kind: sort_kind_from_pb(b.kind), - reverse: b.reverse, - }, - None => SortMode::FromConfig, - } - } else { - SortMode::FromConfig - }; - let cids = col.search_cards(&input.search, order)?; - Ok(pb::SearchCardsOut { - card_ids: cids.into_iter().map(|v| v.0).collect(), - }) - }) - } - - fn search_notes(&mut self, input: pb::SearchNotesIn) -> Result { - self.with_col(|col| { - let nids = col.search_notes(&input.search)?; - Ok(pb::SearchNotesOut { - note_ids: nids.into_iter().map(|v| v.0).collect(), - }) - }) - } - - fn local_minutes_west(&mut self, input: pb::Int64) -> BackendResult { - Ok(pb::Int32 { - val: local_minutes_west_for_stamp(input.val), - }) - } - fn strip_av_tags(&mut self, input: pb::String) -> BackendResult { Ok(pb::String { val: strip_av_tags(&input.val).into(), @@ -335,14 +284,67 @@ impl BackendService for Backend { }) } - fn check_database(&mut self, _input: pb::Empty) -> BackendResult { + // searching + //----------------------------------------------- + + fn search_cards(&mut self, input: pb::SearchCardsIn) -> Result { self.with_col(|col| { - col.check_database().map(|problems| pb::CheckDatabaseOut { - problems: problems.to_i18n_strings(&col.i18n), + let order = if let Some(order) = input.order { + use pb::sort_order::Value as V; + match order.value { + Some(V::None(_)) => SortMode::NoOrder, + Some(V::Custom(s)) => SortMode::Custom(s), + Some(V::FromConfig(_)) => SortMode::FromConfig, + Some(V::Builtin(b)) => SortMode::Builtin { + kind: sort_kind_from_pb(b.kind), + reverse: b.reverse, + }, + None => SortMode::FromConfig, + } + } else { + SortMode::FromConfig + }; + let cids = col.search_cards(&input.search, order)?; + Ok(pb::SearchCardsOut { + card_ids: cids.into_iter().map(|v| v.0).collect(), }) }) } + fn search_notes(&mut self, input: pb::SearchNotesIn) -> Result { + self.with_col(|col| { + let nids = col.search_notes(&input.search)?; + Ok(pb::SearchNotesOut { + note_ids: nids.into_iter().map(|v| v.0).collect(), + }) + }) + } + + // scheduling + //----------------------------------------------- + + fn sched_timing_today(&mut self, _input: pb::Empty) -> Result { + self.with_col(|col| col.timing_today().map(Into::into)) + } + + fn local_minutes_west(&mut self, input: pb::Int64) -> BackendResult { + Ok(pb::Int32 { + val: local_minutes_west_for_stamp(input.val), + }) + } + + // decks + //----------------------------------------------- + + fn deck_tree(&mut self, input: pb::DeckTreeIn) -> Result { + let lim = if input.top_deck_id > 0 { + Some(DeckID(input.top_deck_id)) + } else { + None + }; + self.with_col(|col| col.deck_tree(input.include_counts, lim)) + } + fn deck_tree_legacy(&mut self, _input: pb::Empty) -> BackendResult { self.with_col(|col| { let tree = col.legacy_deck_tree()?; @@ -352,26 +354,6 @@ impl BackendService for Backend { }) } - fn get_empty_cards(&mut self, _input: pb::Empty) -> Result { - self.with_col(|col| { - let mut empty = col.empty_cards()?; - let report = col.empty_cards_report(&mut empty)?; - - let mut outnotes = vec![]; - for (_ntid, notes) in empty { - outnotes.extend(notes.into_iter().map(|e| pb::NoteWithEmptyCards { - note_id: e.nid.0, - will_delete_note: e.empty.len() == e.current_count, - card_ids: e.empty.into_iter().map(|(_ord, id)| id.0).collect(), - })) - } - Ok(pb::EmptyCardsReport { - report, - notes: outnotes, - }) - }) - } - fn get_deck_legacy(&mut self, input: pb::Int64) -> Result { self.with_col(|col| { let deck: DeckSchema11 = col @@ -393,6 +375,120 @@ impl BackendService for Backend { }) } + fn get_all_decks_legacy(&mut self, _input: Empty) -> BackendResult { + self.with_col(|col| { + let decks = col.storage.get_all_decks_as_schema11()?; + serde_json::to_vec(&decks).map_err(Into::into) + }) + .map(Into::into) + } + + fn get_deck_names(&mut self, input: pb::GetDeckNamesIn) -> Result { + self.with_col(|col| { + let names = if input.include_filtered { + col.get_all_deck_names(input.skip_empty_default)? + } else { + col.get_all_normal_deck_names()? + }; + Ok(pb::DeckNames { + entries: names + .into_iter() + .map(|(id, name)| pb::DeckNameId { id: id.0, name }) + .collect(), + }) + }) + } + + fn add_or_update_deck_legacy( + &mut self, + input: pb::AddOrUpdateDeckLegacyIn, + ) -> Result { + self.with_col(|col| { + let schema11: DeckSchema11 = serde_json::from_slice(&input.deck)?; + let mut deck: Deck = schema11.into(); + if input.preserve_usn_and_mtime { + col.transact(None, |col| { + let usn = col.usn()?; + col.add_or_update_single_deck(&mut deck, usn) + })?; + } else { + col.add_or_update_deck(&mut deck)?; + } + Ok(deck.id.0.into()) + }) + } + + fn new_deck_legacy(&mut self, input: pb::Bool) -> BackendResult { + let deck = if input.val { + Deck::new_filtered() + } else { + Deck::new_normal() + }; + let schema11: DeckSchema11 = deck.into(); + serde_json::to_vec(&schema11) + .map_err(Into::into) + .map(Into::into) + } + + fn remove_deck(&mut self, input: pb::Int64) -> BackendResult { + self.with_col(|col| col.remove_deck_and_child_decks(DeckID(input.val))) + .map(Into::into) + } + + // deck config + //---------------------------------------------------- + + fn add_or_update_deck_config_legacy( + &mut self, + input: AddOrUpdateDeckConfigLegacyIn, + ) -> BackendResult { + let conf: DeckConfSchema11 = serde_json::from_slice(&input.config)?; + let mut conf: DeckConf = conf.into(); + self.with_col(|col| { + col.transact(None, |col| { + col.add_or_update_deck_config(&mut conf, input.preserve_usn_and_mtime)?; + Ok(conf.id.0) + }) + }) + .map(Into::into) + } + + fn all_deck_config_legacy(&mut self, _input: Empty) -> BackendResult { + self.with_col(|col| { + let conf: Vec = col + .storage + .all_deck_config()? + .into_iter() + .map(Into::into) + .collect(); + serde_json::to_vec(&conf).map_err(Into::into) + }) + .map(Into::into) + } + + fn new_deck_config_legacy(&mut self, _input: Empty) -> BackendResult { + serde_json::to_vec(&DeckConfSchema11::default()) + .map_err(Into::into) + .map(Into::into) + } + + fn remove_deck_config(&mut self, input: pb::Int64) -> BackendResult { + self.with_col(|col| col.transact(None, |col| col.remove_deck_config(DeckConfID(input.val)))) + .map(Into::into) + } + + fn get_deck_config_legacy(&mut self, input: pb::Int64) -> BackendResult { + self.with_col(|col| { + let conf = col.get_deck_config(DeckConfID(input.val), true)?.unwrap(); + let conf: DeckConfSchema11 = conf.into(); + Ok(serde_json::to_vec(&conf)?) + }) + .map(Into::into) + } + + // media + //------------------------------------------------------------------- + fn sync_media(&mut self, input: SyncMediaIn) -> BackendResult { let mut guard = self.col.lock().unwrap(); @@ -420,6 +516,38 @@ impl BackendService for Backend { }) .map(Into::into) } + + fn check_media(&mut self, _input: pb::Empty) -> Result { + let callback = + |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); + + self.with_col(|col| { + let mgr = MediaManager::new(&col.media_folder, &col.media_db)?; + col.transact(None, |ctx| { + let mut checker = MediaChecker::new(ctx, &mgr, callback); + let mut output = checker.check()?; + + let report = checker.summarize_output(&mut output); + + Ok(pb::CheckMediaOut { + unused: output.unused, + missing: output.missing, + report, + have_trash: output.trash_count > 0, + }) + }) + }) + } + + // misc + + fn check_database(&mut self, _input: pb::Empty) -> BackendResult { + self.with_col(|col| { + col.check_database().map(|problems| pb::CheckDatabaseOut { + problems: problems.to_i18n_strings(&col.i18n), + }) + }) + } } impl Backend { @@ -547,16 +675,6 @@ impl Backend { OValue::UpdateCard(pb::Empty {}) } Value::AddCard(card) => OValue::AddCard(self.add_card(card)?), - Value::GetDeckConfig(dcid) => OValue::GetDeckConfig(self.get_deck_config(dcid)?), - Value::AddOrUpdateDeckConfig(input) => { - OValue::AddOrUpdateDeckConfig(self.add_or_update_deck_config(input)?) - } - Value::AllDeckConfig(_) => OValue::AllDeckConfig(self.all_deck_config()?), - Value::NewDeckConfig(_) => OValue::NewDeckConfig(self.new_deck_config()?), - Value::RemoveDeckConfig(dcid) => { - self.remove_deck_config(dcid)?; - OValue::RemoveDeckConfig(pb::Empty {}) - } Value::AbortMediaSync(_) => { self.abort_media_sync(); OValue::AbortMediaSync(pb::Empty {}) @@ -582,7 +700,6 @@ impl Backend { Value::GetChangedNotetypes(_) => { OValue::GetChangedNotetypes(self.get_changed_notetypes()?) } - Value::GetAllDecks(_) => OValue::GetAllDecks(self.get_all_decks()?), Value::GetStockNotetypeLegacy(kind) => { OValue::GetStockNotetypeLegacy(self.get_stock_notetype_legacy(kind)?) } @@ -611,18 +728,6 @@ impl Backend { OValue::UpdateNote(pb::Empty {}) } Value::GetNote(nid) => OValue::GetNote(self.get_note(nid)?), - Value::GetDeckNames(input) => OValue::GetDeckNames(self.get_deck_names(input)?), - Value::AddOrUpdateDeckLegacy(input) => { - OValue::AddOrUpdateDeckLegacy(self.add_or_update_deck_legacy(input)?) - } - Value::NewDeckLegacy(filtered) => { - OValue::NewDeckLegacy(self.new_deck_legacy(filtered)?) - } - - Value::RemoveDeck(did) => OValue::RemoveDeck({ - self.remove_deck(did)?; - pb::Empty {} - }), Value::FieldNamesForNotes(input) => { OValue::FieldNamesForNotes(self.field_names_for_notes(input)?) } @@ -845,45 +950,6 @@ impl Backend { Ok(card.id.0) } - fn get_deck_config(&self, dcid: i64) -> Result> { - self.with_col(|col| { - let conf = col.get_deck_config(DeckConfID(dcid), true)?.unwrap(); - let conf: DeckConfSchema11 = conf.into(); - Ok(serde_json::to_vec(&conf)?) - }) - } - - fn add_or_update_deck_config(&self, input: AddOrUpdateDeckConfigIn) -> Result { - let conf: DeckConfSchema11 = serde_json::from_slice(&input.config)?; - let mut conf: DeckConf = conf.into(); - self.with_col(|col| { - col.transact(None, |col| { - col.add_or_update_deck_config(&mut conf, input.preserve_usn_and_mtime)?; - Ok(conf.id.0) - }) - }) - } - - fn all_deck_config(&self) -> Result> { - self.with_col(|col| { - let conf: Vec = col - .storage - .all_deck_config()? - .into_iter() - .map(Into::into) - .collect(); - serde_json::to_vec(&conf).map_err(Into::into) - }) - } - - fn new_deck_config(&self) -> Result> { - serde_json::to_vec(&DeckConfSchema11::default()).map_err(Into::into) - } - - fn remove_deck_config(&self, dcid: i64) -> Result<()> { - self.with_col(|col| col.transact(None, |col| col.remove_deck_config(DeckConfID(dcid)))) - } - fn before_upload(&self) -> Result<()> { self.with_col(|col| col.before_upload()) } @@ -974,13 +1040,6 @@ impl Backend { // }) } - fn get_all_decks(&self) -> Result> { - self.with_col(|col| { - let decks = col.storage.get_all_decks_as_schema11()?; - serde_json::to_vec(&decks).map_err(Into::into) - }) - } - fn get_notetype_names(&self) -> Result { self.with_col(|col| { let entries: Vec<_> = col @@ -1078,52 +1137,6 @@ impl Backend { }) } - fn get_deck_names(&self, input: pb::GetDeckNamesIn) -> Result { - self.with_col(|col| { - let names = if input.include_filtered { - col.get_all_deck_names(input.skip_empty_default)? - } else { - col.get_all_normal_deck_names()? - }; - Ok(pb::DeckNames { - entries: names - .into_iter() - .map(|(id, name)| pb::DeckNameId { id: id.0, name }) - .collect(), - }) - }) - } - - fn add_or_update_deck_legacy(&self, input: pb::AddOrUpdateDeckLegacyIn) -> Result { - self.with_col(|col| { - let schema11: DeckSchema11 = serde_json::from_slice(&input.deck)?; - let mut deck: Deck = schema11.into(); - if input.preserve_usn_and_mtime { - col.transact(None, |col| { - let usn = col.usn()?; - col.add_or_update_single_deck(&mut deck, usn) - })?; - } else { - col.add_or_update_deck(&mut deck)?; - } - Ok(deck.id.0) - }) - } - - fn new_deck_legacy(&self, filtered: bool) -> Result> { - let deck = if filtered { - Deck::new_filtered() - } else { - Deck::new_normal() - }; - let schema11: DeckSchema11 = deck.into(); - serde_json::to_vec(&schema11).map_err(Into::into) - } - - fn remove_deck(&self, did: i64) -> Result<()> { - self.with_col(|col| col.remove_deck_and_child_decks(DeckID(did))) - } - fn field_names_for_notes( &self, input: pb::FieldNamesForNotesIn,