From 94a72f970a1d6ac8c812e6f9014f4557f4011dbc Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 11 Jan 2020 09:35:41 +1000 Subject: [PATCH] type: and hint: support We may need to keep handling hints in the Python code for now until i18n is sorted out. --- rslib/Cargo.toml | 2 + rslib/src/template.rs | 2 +- rslib/src/template_filters.rs | 98 ++++++++++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 2e0582d8b..04c7a766d 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -13,6 +13,8 @@ bytes = "0.4" chrono = "0.4.10" lazy_static = "1.4.0" regex = "1.3.3" +hex = "0.4.0" +blake3 = "0.1.0" [build-dependencies] prost-build = "0.5.0" diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 06cc7d554..71b086b16 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -290,7 +290,7 @@ fn render_into( } Replacement { key, filters } => { let (text, remaining_filters) = match fields.get(key) { - Some(text) => apply_filters(text, filters), + Some(text) => apply_filters(text, filters, key), None => (unknown_field_message(key, filters).into(), vec![]), }; diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs index 15e39088a..586766a25 100644 --- a/rslib/src/template_filters.rs +++ b/rslib/src/template_filters.rs @@ -2,6 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use crate::text::strip_html; +use blake3::Hasher; use lazy_static::lazy_static; use regex::{Captures, Regex}; use std::borrow::Cow; @@ -13,11 +14,22 @@ use std::borrow::Cow; /// /// The first non-standard filter that is encountered will terminate processing, /// so non-standard filters must come at the end. -pub(crate) fn apply_filters<'a>(text: &'a str, filters: &[&str]) -> (Cow<'a, str>, Vec) { +pub(crate) fn apply_filters<'a>( + text: &'a str, + filters: &[&str], + field_name: &str, +) -> (Cow<'a, str>, Vec) { let mut text: Cow = text.into(); + // type:cloze is handled specially + let filters = if filters == ["type", "cloze"] { + &["type-cloze"] + } else { + filters + }; + for (idx, &filter_name) in filters.iter().enumerate() { - match apply_filter(filter_name, text.as_ref()) { + match apply_filter(filter_name, text.as_ref(), field_name) { (true, None) => { // filter did not change text } @@ -43,13 +55,22 @@ pub(crate) fn apply_filters<'a>(text: &'a str, filters: &[&str]) -> (Cow<'a, str /// /// Returns true if filter was valid. /// Returns string if input text changed. -fn apply_filter<'a>(filter_name: &str, text: &'a str) -> (bool, Option) { +fn apply_filter<'a>(filter_name: &str, text: &'a str, field_name: &str) -> (bool, Option) { let output_text = match filter_name { "text" => strip_html(text), "furigana" => furigana_filter(text), "kanji" => kanji_filter(text), "kana" => kana_filter(text), - _ => return (false, None), + other => { + let split: Vec<_> = other.splitn(2, '-').collect(); + let base = split[0]; + let filter_args = *split.get(1).unwrap_or(&""); + match base { + "type" => type_filter(text, filter_args, field_name), + "hint" => hint_filter(text, field_name), + _ => return (false, None), + } + } }; ( @@ -122,15 +143,50 @@ fn furigana_filter(text: &str) -> Cow { // Other filters //---------------------------------------- -// - type -// - hint +/// convert to [[type:...]] for the gui code to process +fn type_filter<'a>(_text: &'a str, filter_args: &str, field_name: &str) -> Cow<'a, str> { + if filter_args.is_empty() { + format!("[[type:{}]]", field_name) + } else { + format!("[[type:{}:{}]]", filter_args, field_name) + } + .into() +} + +// fixme: i18n +fn hint_filter<'a>(text: &'a str, field_name: &str) -> Cow<'a, str> { + if text.trim().is_empty() { + return text.into(); + } + + // generate a unique DOM id + let mut hasher = Hasher::new(); + hasher.update(text.as_bytes()); + hasher.update(field_name.as_bytes()); + let id = hex::encode(&hasher.finalize().as_bytes()[0..8]); + + format!( + r##" + +{} + +"##, + id, text, id, field_name + ) + .into() +} // Tests //---------------------------------------- #[cfg(test)] mod test { - use crate::template_filters::{furigana_filter, kana_filter, kanji_filter}; + use crate::template_filters::{ + apply_filters, furigana_filter, hint_filter, kana_filter, kanji_filter, type_filter, + }; #[test] fn test_furigana() { @@ -142,4 +198,32 @@ mod test { "firstsecond" ); } + + #[test] + fn test_hint() { + assert_eq!( + hint_filter("foo", "field"), + r##" + +foo + +"## + ); + } + + #[test] + fn test_type() { + assert_eq!(type_filter("ignored", "", "Front"), "[[type:Front]]"); + assert_eq!( + type_filter("ignored", "cloze", "Front"), + "[[type:cloze:Front]]" + ); + assert_eq!( + apply_filters("ignored", &["type", "cloze"], "Text"), + ("[[type:cloze:Text]]".into(), vec![]) + ); + } }