mirror of
https://github.com/ankitects/anki.git
synced 2025-11-20 11:37:12 -05:00
* Add crate snafu * Replace all inline structs in AnkiError * Derive Snafu on AnkiError * Use snafu for card type errors * Use snafu whatever error for InvalidInput * Use snafu for NotFoundError and improve message * Use snafu for FileIoError to attach context Remove IoError. Add some context-attaching helpers to replace code returning bare io::Errors. * Add more context-attaching io helpers * Add message, context and backtrace to new snafus * Utilize error context and backtrace on frontend * Rename LocalizedError -> BackendError. * Remove DocumentedError. * Have all backend exceptions inherit BackendError. * Rename localized(_description) -> message * Remove accidentally committed experimental trait * invalid_input_context -> ok_or_invalid * ensure_valid_input! -> require! * Always return `Err` from `invalid_input!` Instead of a Result to unwrap, the macro accepts a source error now. * new_tempfile_in_parent -> new_tempfile_in_parent_of * ok_or_not_found -> or_not_found * ok_or_invalid -> or_invalid * Add crate convert_case * Use unqualified lowercase type name * Remove uses of snafu::ensure * Allow public construction of InvalidInputErrors (dae) Needed to port the AnkiDroid changes. * Make into_protobuf() public (dae) Also required for AnkiDroid. Not sure why it worked previously - possible bug in older Rust version?
184 lines
6.1 KiB
Rust
184 lines
6.1 KiB
Rust
// Copyright: Ankitects Pty Ltd and contributors
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
use super::Backend;
|
|
pub(super) use crate::pb::cardrendering_service::Service as CardRenderingService;
|
|
use crate::{
|
|
card_rendering::{extract_av_tags, strip_av_tags},
|
|
latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex},
|
|
markdown::render_markdown,
|
|
notetype::{CardTemplateSchema11, RenderCardOutput},
|
|
pb,
|
|
prelude::*,
|
|
template::RenderedNode,
|
|
text::{
|
|
decode_iri_paths, encode_iri_paths, sanitize_html_no_images, strip_html,
|
|
strip_html_preserving_media_filenames,
|
|
},
|
|
typeanswer::compare_answer,
|
|
};
|
|
|
|
impl CardRenderingService for Backend {
|
|
fn extract_av_tags(
|
|
&self,
|
|
input: pb::ExtractAvTagsRequest,
|
|
) -> Result<pb::ExtractAvTagsResponse> {
|
|
let out = extract_av_tags(input.text, input.question_side, self.i18n());
|
|
Ok(pb::ExtractAvTagsResponse {
|
|
text: out.0,
|
|
av_tags: out.1,
|
|
})
|
|
}
|
|
|
|
fn extract_latex(&self, input: pb::ExtractLatexRequest) -> Result<pb::ExtractLatexResponse> {
|
|
let func = if input.expand_clozes {
|
|
extract_latex_expanding_clozes
|
|
} else {
|
|
extract_latex
|
|
};
|
|
let (text, extracted) = func(&input.text, input.svg);
|
|
|
|
Ok(pb::ExtractLatexResponse {
|
|
text,
|
|
latex: extracted
|
|
.into_iter()
|
|
.map(|e: ExtractedLatex| pb::ExtractedLatex {
|
|
filename: e.fname,
|
|
latex_body: e.latex,
|
|
})
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
fn get_empty_cards(&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::empty_cards_report::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 render_existing_card(
|
|
&self,
|
|
input: pb::RenderExistingCardRequest,
|
|
) -> Result<pb::RenderCardResponse> {
|
|
self.with_col(|col| {
|
|
col.render_existing_card(CardId(input.card_id), input.browser)
|
|
.map(Into::into)
|
|
})
|
|
}
|
|
|
|
fn render_uncommitted_card(
|
|
&self,
|
|
input: pb::RenderUncommittedCardRequest,
|
|
) -> Result<pb::RenderCardResponse> {
|
|
let template = input.template.or_invalid("missing template")?.into();
|
|
let mut note = input.note.or_invalid("missing note")?.into();
|
|
let ord = input.card_ord as u16;
|
|
let fill_empty = input.fill_empty;
|
|
self.with_col(|col| {
|
|
col.render_uncommitted_card(&mut note, &template, ord, fill_empty)
|
|
.map(Into::into)
|
|
})
|
|
}
|
|
|
|
fn render_uncommitted_card_legacy(
|
|
&self,
|
|
input: pb::RenderUncommittedCardLegacyRequest,
|
|
) -> Result<pb::RenderCardResponse> {
|
|
let schema11: CardTemplateSchema11 = serde_json::from_slice(&input.template)?;
|
|
let template = schema11.into();
|
|
let mut note = input.note.or_invalid("missing note")?.into();
|
|
let ord = input.card_ord as u16;
|
|
let fill_empty = input.fill_empty;
|
|
self.with_col(|col| {
|
|
col.render_uncommitted_card(&mut note, &template, ord, fill_empty)
|
|
.map(Into::into)
|
|
})
|
|
}
|
|
|
|
fn strip_av_tags(&self, input: pb::String) -> Result<pb::String> {
|
|
Ok(strip_av_tags(input.val).into())
|
|
}
|
|
|
|
fn render_markdown(&self, input: pb::RenderMarkdownRequest) -> Result<pb::String> {
|
|
let mut text = render_markdown(&input.markdown);
|
|
if input.sanitize {
|
|
// currently no images
|
|
text = sanitize_html_no_images(&text);
|
|
}
|
|
Ok(text.into())
|
|
}
|
|
|
|
fn encode_iri_paths(&self, input: pb::String) -> Result<pb::String> {
|
|
Ok(encode_iri_paths(&input.val).to_string().into())
|
|
}
|
|
|
|
fn decode_iri_paths(&self, input: pb::String) -> Result<pb::String> {
|
|
Ok(decode_iri_paths(&input.val).to_string().into())
|
|
}
|
|
|
|
fn strip_html(&self, input: pb::StripHtmlRequest) -> Result<pb::String> {
|
|
Ok(match input.mode() {
|
|
pb::strip_html_request::Mode::Normal => strip_html(&input.text),
|
|
pb::strip_html_request::Mode::PreserveMediaFilenames => {
|
|
strip_html_preserving_media_filenames(&input.text)
|
|
}
|
|
}
|
|
.to_string()
|
|
.into())
|
|
}
|
|
|
|
fn compare_answer(&self, input: pb::CompareAnswerRequest) -> Result<pb::String> {
|
|
Ok(compare_answer(&input.expected, &input.provided).into())
|
|
}
|
|
}
|
|
|
|
fn rendered_nodes_to_proto(nodes: Vec<RenderedNode>) -> Vec<pb::RenderedTemplateNode> {
|
|
nodes
|
|
.into_iter()
|
|
.map(|n| pb::RenderedTemplateNode {
|
|
value: Some(rendered_node_to_proto(n)),
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn rendered_node_to_proto(node: RenderedNode) -> pb::rendered_template_node::Value {
|
|
match node {
|
|
RenderedNode::Text { text } => pb::rendered_template_node::Value::Text(text),
|
|
RenderedNode::Replacement {
|
|
field_name,
|
|
current_text,
|
|
filters,
|
|
} => pb::rendered_template_node::Value::Replacement(pb::RenderedTemplateReplacement {
|
|
field_name,
|
|
current_text,
|
|
filters,
|
|
}),
|
|
}
|
|
}
|
|
|
|
impl From<RenderCardOutput> for pb::RenderCardResponse {
|
|
fn from(o: RenderCardOutput) -> Self {
|
|
pb::RenderCardResponse {
|
|
question_nodes: rendered_nodes_to_proto(o.qnodes),
|
|
answer_nodes: rendered_nodes_to_proto(o.anodes),
|
|
css: o.css,
|
|
latex_svg: o.latex_svg,
|
|
}
|
|
}
|
|
}
|