migrate decks and dconf methods

This commit is contained in:
Damien Elmes 2020-05-23 15:09:16 +10:00
parent 081a61a438
commit 7550e6241c
4 changed files with 415 additions and 380 deletions

View file

@ -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;
}

View file

@ -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)

View file

@ -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@@

View file

@ -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,78 +203,24 @@ impl BackendService for Backend {
})
}
fn sched_timing_today(&mut self, _input: pb::Empty) -> Result<pb::SchedTimingTodayOut> {
self.with_col(|col| col.timing_today().map(Into::into))
}
fn deck_tree(&mut self, input: pb::DeckTreeIn) -> Result<pb::DeckTreeNode> {
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<pb::CheckMediaOut> {
let callback =
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
fn get_empty_cards(&mut self, _input: pb::Empty) -> Result<pb::EmptyCardsReport> {
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,
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,
have_trash: output.trash_count > 0,
notes: outnotes,
})
})
})
}
fn search_cards(&mut self, input: pb::SearchCardsIn) -> Result<pb::SearchCardsOut> {
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<pb::SearchNotesOut> {
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<pb::Int32> {
Ok(pb::Int32 {
val: local_minutes_west_for_stamp(input.val),
})
}
fn strip_av_tags(&mut self, input: pb::String) -> BackendResult<pb::String> {
@ -335,14 +284,67 @@ impl BackendService for Backend {
})
}
fn check_database(&mut self, _input: pb::Empty) -> BackendResult<pb::CheckDatabaseOut> {
// searching
//-----------------------------------------------
fn search_cards(&mut self, input: pb::SearchCardsIn) -> Result<pb::SearchCardsOut> {
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<pb::SearchNotesOut> {
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<pb::SchedTimingTodayOut> {
self.with_col(|col| col.timing_today().map(Into::into))
}
fn local_minutes_west(&mut self, input: pb::Int64) -> BackendResult<pb::Int32> {
Ok(pb::Int32 {
val: local_minutes_west_for_stamp(input.val),
})
}
// decks
//-----------------------------------------------
fn deck_tree(&mut self, input: pb::DeckTreeIn) -> Result<pb::DeckTreeNode> {
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<pb::Bytes> {
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<pb::EmptyCardsReport> {
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<pb::Bytes> {
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<pb::Bytes> {
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<pb::DeckNames> {
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<pb::Int64> {
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<pb::Bytes> {
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<Empty> {
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<pb::Int64> {
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<pb::Bytes> {
self.with_col(|col| {
let conf: Vec<DeckConfSchema11> = 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<pb::Bytes> {
serde_json::to_vec(&DeckConfSchema11::default())
.map_err(Into::into)
.map(Into::into)
}
fn remove_deck_config(&mut self, input: pb::Int64) -> BackendResult<Empty> {
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<pb::Bytes> {
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<Empty> {
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<pb::CheckMediaOut> {
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<pb::CheckDatabaseOut> {
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<Vec<u8>> {
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<i64> {
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<Vec<u8>> {
self.with_col(|col| {
let conf: Vec<DeckConfSchema11> = 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<Vec<u8>> {
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<Vec<u8>> {
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<pb::NoteTypeNames> {
self.with_col(|col| {
let entries: Vec<_> = col
@ -1078,52 +1137,6 @@ impl Backend {
})
}
fn get_deck_names(&self, input: pb::GetDeckNamesIn) -> Result<pb::DeckNames> {
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<i64> {
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<Vec<u8>> {
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,