From 2dd0ef09df37de468ed0c64e03ea4b967ebbfc11 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Wed, 15 Dec 2021 08:47:50 +0100 Subject: [PATCH] Replace old `extract_av_tags` and `strip_av_tags` ... with new `card_rendering` mod --- rslib/src/backend/cardrendering.rs | 39 +++----------- rslib/src/browser_table.rs | 7 +-- rslib/src/template.rs | 1 - rslib/src/template_filters.rs | 32 ++++------- rslib/src/text.rs | 86 ------------------------------ 5 files changed, 20 insertions(+), 145 deletions(-) diff --git a/rslib/src/backend/cardrendering.rs b/rslib/src/backend/cardrendering.rs index 6606c7ed1..0f24ab8a1 100644 --- a/rslib/src/backend/cardrendering.rs +++ b/rslib/src/backend/cardrendering.rs @@ -5,14 +5,15 @@ use super::Backend; pub(super) use crate::backend_proto::cardrendering_service::Service as CardRenderingService; use crate::{ backend_proto as pb, + card_rendering::{extract_av_tags, strip_av_tags}, latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex}, markdown::render_markdown, notetype::{CardTemplateSchema11, RenderCardOutput}, prelude::*, template::RenderedNode, text::{ - decode_iri_paths, encode_iri_paths, extract_av_tags, sanitize_html_no_images, - strip_av_tags, strip_html, strip_html_preserving_media_filenames, AvTag, + decode_iri_paths, encode_iri_paths, sanitize_html_no_images, strip_html, + strip_html_preserving_media_filenames, }, }; @@ -21,34 +22,10 @@ impl CardRenderingService for Backend { &self, input: pb::ExtractAvTagsRequest, ) -> Result { - let (text, tags) = extract_av_tags(&input.text, input.question_side); - let pt_tags = tags - .into_iter() - .map(|avtag| match avtag { - AvTag::SoundOrVideo(file) => pb::AvTag { - value: Some(pb::av_tag::Value::SoundOrVideo(file)), - }, - AvTag::TextToSpeech { - field_text, - lang, - voices, - other_args, - speed, - } => pb::AvTag { - value: Some(pb::av_tag::Value::Tts(pb::TtsTag { - field_text, - lang, - voices, - speed, - other_args, - })), - }, - }) - .collect(); - + let out = extract_av_tags(&input.text, input.question_side, self.i18n()); Ok(pb::ExtractAvTagsResponse { - text: text.into(), - av_tags: pt_tags, + text: out.0, + av_tags: out.1, }) } @@ -140,9 +117,7 @@ impl CardRenderingService for Backend { } fn strip_av_tags(&self, input: pb::String) -> Result { - Ok(pb::String { - val: strip_av_tags(&input.val).into(), - }) + Ok(strip_av_tags(&input.val).into()) } fn render_markdown(&self, input: pb::RenderMarkdownRequest) -> Result { diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index c1fe4a3cd..349ca2c05 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -9,11 +9,12 @@ use strum::{Display, EnumIter, EnumString, IntoEnumIterator}; use crate::{ backend_proto as pb, card::{CardQueue, CardType}, + card_rendering::extract_av_tags, notetype::{CardTemplate, NotetypeKind}, prelude::*, scheduler::{timespan::time_span, timing::SchedTimingToday}, template::RenderedNode, - text::{extract_av_tags, html_to_text_line}, + text::html_to_text_line, }; #[derive(Debug, PartialEq, Clone, Copy, Display, EnumIter, EnumString)] @@ -270,7 +271,7 @@ impl RenderContext { } => current_text, }) .join(""); - let question = extract_av_tags(&qnodes_text, true).0.to_string(); + let question = extract_av_tags(&qnodes_text, true, &col.tr).0; Ok(RenderContext { question, @@ -410,7 +411,7 @@ impl RowContext { } => current_text, }) .join(""); - let answer = extract_av_tags(&answer, false).0; + let answer = extract_av_tags(&answer, false, &self.tr).0; html_to_text_line( if let Some(stripped) = answer.strip_prefix(&render_context.question) { stripped diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 9628033df..876a276e0 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -443,7 +443,6 @@ fn render_into( .as_slice(), key, context, - tr, ), None => { // unknown field encountered diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs index 81b002668..b799983b3 100644 --- a/rslib/src/template_filters.rs +++ b/rslib/src/template_filters.rs @@ -9,7 +9,6 @@ use regex::{Captures, Regex}; use crate::{ cloze::{cloze_filter, cloze_only_filter}, - i18n::I18n, template::RenderContext, text::strip_html, }; @@ -26,7 +25,6 @@ pub(crate) fn apply_filters<'a>( filters: &[&str], field_name: &str, context: &RenderContext, - tr: &I18n, ) -> (Cow<'a, str>, Vec) { let mut text: Cow = text.into(); @@ -38,7 +36,7 @@ pub(crate) fn apply_filters<'a>( }; for (idx, &filter_name) in filters.iter().enumerate() { - match apply_filter(filter_name, text.as_ref(), field_name, context, tr) { + match apply_filter(filter_name, text.as_ref(), field_name, context) { (true, None) => { // filter did not change text } @@ -69,7 +67,6 @@ fn apply_filter<'a>( text: &'a str, field_name: &str, context: &RenderContext, - tr: &I18n, ) -> (bool, Option) { let output_text = match filter_name { "text" => strip_html(text), @@ -84,8 +81,8 @@ fn apply_filter<'a>( // an empty filter name (caused by using two colons) is ignored "" => text.into(), _ => { - if filter_name.starts_with("tts ") { - tts_filter(filter_name, text, tr) + if let Some(options) = filter_name.strip_prefix("tts ") { + tts_filter(options, text).into() } else { // unrecognized filter return (false, None); @@ -194,12 +191,10 @@ return false;"> .into() } -fn tts_filter(filter_name: &str, text: &str, tr: &I18n) -> Cow<'static, str> { - let args = filter_name.split_once(' ').map_or("", |t| t.1); - let text = text.replace("[...]", &tr.card_templates_blank()); - - format!("[anki:tts][{}]{}[/anki:tts]", args, text).into() +fn tts_filter(options: &str, text: &str) -> String { + format!("[anki:tts lang={}]{}[/anki:tts]", options, text) } + // Tests //---------------------------------------- @@ -235,7 +230,6 @@ field #[test] fn typing() { - let tr = I18n::template_only(); assert_eq!(type_filter("Front"), "[[type:Front]]"); assert_eq!(type_cloze_filter("Front"), "[[type:cloze:Front]]"); let ctx = RenderContext { @@ -245,7 +239,7 @@ field card_ord: 0, }; assert_eq!( - apply_filters("ignored", &["cloze", "type"], "Text", &ctx, &tr), + apply_filters("ignored", &["cloze", "type"], "Text", &ctx), ("[[type:cloze:Text]]".into(), vec![]) ); } @@ -280,17 +274,9 @@ field #[test] fn tts() { - let tr = I18n::template_only(); assert_eq!( - tts_filter("tts en_US voices=Bob,Jane", "foo", &tr), - "[anki:tts][en_US voices=Bob,Jane]foo[/anki:tts]" - ); - assert_eq!( - tts_filter("tts en_US", "foo [...]", &tr), - format!( - "[anki:tts][en_US]foo {}[/anki:tts]", - tr.card_templates_blank() - ) + tts_filter("en_US voices=Bob,Jane", "foo"), + "[anki:tts lang=en_US voices=Bob,Jane]foo[/anki:tts]" ); } } diff --git a/rslib/src/text.rs b/rslib/src/text.rs index 02e7d3321..ff2d6ed54 100644 --- a/rslib/src/text.rs +++ b/rslib/src/text.rs @@ -175,32 +175,6 @@ pub fn strip_html_for_tts(html: &str) -> Cow { out } -pub fn strip_av_tags(text: &str) -> Cow { - AV_TAGS.replace_all(text, "") -} - -/// Extract audio tags from string, replacing them with [anki:play] refs -pub fn extract_av_tags(text: &str, question_side: bool) -> (Cow, Vec) { - let mut tags = vec![]; - let context = if question_side { 'q' } else { 'a' }; - let replaced_text = AV_TAGS.replace_all(text, |caps: &Captures| { - // extract - let tag = if let Some(av_file) = caps.get(1) { - AvTag::SoundOrVideo(decode_entities(av_file.as_str()).into()) - } else { - let args = caps.get(2).unwrap(); - let field_text = caps.get(3).unwrap(); - tts_tag_from_string(field_text.as_str(), args.as_str()) - }; - tags.push(tag); - - // and replace with reference - format!("[anki:play:{}:{}]", context, tags.len() - 1) - }); - - (replaced_text, tags) -} - #[derive(Debug)] pub(crate) struct MediaRef<'a> { pub full_ref: &'a str, @@ -242,40 +216,6 @@ pub(crate) fn extract_media_refs(text: &str) -> Vec { out } -fn tts_tag_from_string<'a>(field_text: &'a str, args: &'a str) -> AvTag { - let mut other_args = vec![]; - let mut split_args = args.split_ascii_whitespace(); - let lang = split_args.next().unwrap_or(""); - let mut voices = None; - let mut speed = 1.0; - - for remaining_arg in split_args { - if remaining_arg.starts_with("voices=") { - voices = remaining_arg - .split('=') - .nth(1) - .map(|voices| voices.split(',').map(ToOwned::to_owned).collect()); - } else if remaining_arg.starts_with("speed=") { - speed = remaining_arg - .split('=') - .nth(1) - .unwrap() - .parse() - .unwrap_or(1.0); - } else { - other_args.push(remaining_arg.to_owned()); - } - } - - AvTag::TextToSpeech { - field_text: strip_html_for_tts(field_text).into(), - lang: lang.into(), - voices: voices.unwrap_or_else(Vec::new), - speed, - other_args, - } -} - pub fn strip_html_preserving_media_filenames(html: &str) -> Cow { let without_fnames = HTML_MEDIA_TAGS.replace_all(html, r" ${1}${2}${3} "); let without_html = strip_html(&without_fnames); @@ -497,32 +437,6 @@ mod test { assert_eq!(strip_html_preserving_media_filenames(""), ""); } - #[test] - fn audio() { - let s = concat!( - "abc[sound:fo&obar.mp3]def[anki:tts][en_US voices=Bob,Jane speed=1.2]", - "foo bar
1>2[/anki:tts]gh", - ); - assert_eq!(strip_av_tags(s), "abcdefgh"); - - let (text, tags) = extract_av_tags(s, true); - assert_eq!(text, "abc[anki:play:q:0]def[anki:play:q:1]gh"); - - assert_eq!( - tags, - vec![ - AvTag::SoundOrVideo("fo&obar.mp3".into()), - AvTag::TextToSpeech { - field_text: "foo bar 1>2".into(), - lang: "en_US".into(), - voices: vec!["Bob".into(), "Jane".into()], - other_args: vec![], - speed: 1.2 - }, - ] - ); - } - #[test] fn combining() { assert!(matches!(without_combining("test"), Cow::Borrowed(_)));