diff --git a/Cargo.lock b/Cargo.lock index 19ff500..5fad90e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2502,9 +2502,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick 1.0.1", "memchr", @@ -3279,6 +3279,7 @@ dependencies = [ "poise", "r2d2", "r2d2_sqlite", + "regex", "reqwest", "rocket 0.5.0-rc.3", "rocket_contrib", diff --git a/Cargo.toml b/Cargo.toml index c068c0f..3349ba5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ gettext = "0.4.0" poise = "0.5.5" r2d2 = "0.8.10" r2d2_sqlite = "0.22.0" +regex = "1.8.4" reqwest = "0.11.18" rocket = { version = "=0.5.0-rc.3", features = ["secrets", "json"] } rocket_contrib = { version = "0.4.11", features = ["templates"] } diff --git a/src/main.rs b/src/main.rs index aaec564..0d8d1e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ use routes::*; mod i18n; use i18n::{i18n_filter, load_catalogs}; -use crate::i18n::langs_filter; +use crate::{i18n::langs_filter, utils::furigana_filter}; mod prelude; @@ -106,6 +106,12 @@ async fn rocket() -> Result, rocket::Error> { move |value: &Value, args: &HashMap| { langs_filter(value, args, &langs) }, + ); + engines.tera.register_filter( + "furigana", + move |value: &Value, args: &HashMap| { + furigana_filter(value, args) + }, ) })) .attach(SassFairing::default()) diff --git a/src/models/challenge.rs b/src/models/challenge.rs index 99a9f39..4fcf2a6 100644 --- a/src/models/challenge.rs +++ b/src/models/challenge.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize}; use serde_yaml::Value; -use crate::prelude::*; +use crate::{prelude::*}; #[derive(Serialize, Deserialize)] pub struct Challenge { diff --git a/src/utils/furigana.rs b/src/utils/furigana.rs new file mode 100644 index 0000000..1bc4bec --- /dev/null +++ b/src/utils/furigana.rs @@ -0,0 +1,36 @@ +use std::collections::HashMap; + +use regex::Regex; +use rocket_dyn_templates::tera::{self, Value}; + +pub fn furigana_to_html(text: &str) -> String { + // Original regular expression: \[([^\]]*)\]{([^\}]*)} + // https://regexr.com/6dspa + // https://blog.elnu.com/2022/01/furigana-in-markdown-using-regular-expressions/ + // The regex crate users curly braces {} as repetition qualifiers, + // so { needs to be escaped as \{ + // Curly brace literals \{ need to have their backslash escaped as \\{ + // TODO: Modify so only wraps continuous sections of furigana + let re = Regex::new(r"\[([^\]]*)\]\{([^\\}]*)}").unwrap(); + format!("{}", re.replace_all(text, "$1($2)")) +} + +pub fn furigana_filter(value: &Value, _args: &HashMap) -> tera::Result { + Ok(Value::String(furigana_to_html(value.as_str().expect("The furigana input must be a string")))) +} + +#[cfg(test)] +mod tests { + #[test] + fn furigana_to_html() { + use super::furigana_to_html; + assert_eq!( + furigana_to_html("[振]{ふ}り[仮]{が}[名]{な}"), + "\ + ()り\ + ()\ + ()\ + " + ); + } +} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f8b78ae..5702a3b 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -5,4 +5,7 @@ mod headers; pub use headers::*; mod challenge; -pub use challenge::*; \ No newline at end of file +pub use challenge::*; + +mod furigana; +pub use furigana::*; \ No newline at end of file