use super::gen::FuriParserGen;
use std::fmt::Debug;
#[derive(Clone, Copy)]
pub struct FuriToReadingParser<'a> {
str: &'a str,
to_kana: bool,
kanji_fallback: bool,
}
impl<'a> FuriToReadingParser<'a> {
#[inline]
pub fn new(str: &'a str, to_kana: bool) -> Self {
Self {
str,
to_kana,
kanji_fallback: true,
}
}
pub fn no_kanji_fallback(mut self) -> Self {
self.kanji_fallback = false;
self
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn char_count(&self) -> usize {
if self.str.is_empty() {
return 0;
}
let mut len = 0;
self.run(|s| len += s.chars().count());
len
}
pub fn len(&self) -> usize {
if self.str.is_empty() {
return 0;
}
let mut len = 0;
self.run(|s| len += s.len());
len
}
pub fn parse(&self) -> String {
let mut buf = String::with_capacity(self.str.len().saturating_sub(10));
self.run(|s| buf.push_str(s));
buf
}
pub fn parse_kanji_and_kana(furi: &str) -> (String, Option<String>) {
let mut kana_buf = String::with_capacity(furi.len());
let mut kanji_buf = String::new();
let mut has_kanji = false;
for (txt, is_kanji) in FuriParserGen::new(furi) {
if is_kanji {
if !has_kanji {
has_kanji = true;
kanji_buf = String::with_capacity(furi.len().saturating_sub(10));
kanji_buf.push_str(&kana_buf);
}
let mut part = txt[1..txt.len() - 1].split('|');
let kanji = unsafe { &part.next().unwrap_unchecked() };
let first_kana = part.next();
if first_kana.is_none() {
kana_buf.push_str(txt);
kanji_buf.push_str(txt);
continue;
}
let first_kana = first_kana.unwrap();
if first_kana.trim().is_empty() {
kana_buf.push_str(kanji);
kanji_buf.push_str(kanji);
} else {
kanji_buf.push_str(kanji);
kana_buf.push_str(first_kana);
kana_buf.extend(part);
}
} else {
kana_buf.push_str(txt);
if has_kanji {
kanji_buf.push_str(txt);
}
}
}
(kana_buf, has_kanji.then_some(kanji_buf))
}
fn run<W>(&self, mut w: W)
where
W: FnMut(&str),
{
for (txt, kanji) in FuriParserGen::new(self.str) {
if kanji {
self.accept_kanji(txt, &mut w);
} else {
self.accept_kana(txt, &mut w);
}
}
}
#[inline]
fn accept_kana<W>(&self, block: &str, mut w: W)
where
W: FnMut(&str),
{
w(block)
}
fn accept_kanji<W>(&self, block: &str, mut w: W)
where
W: FnMut(&str),
{
if !block.contains('|') {
w(block);
return;
}
let block_inner = &block[1..block.len() - 1];
if self.to_kana {
self.parse_kana_part(block_inner, w);
} else {
self.parse_kanji(block_inner, w);
}
}
fn parse_kanji<W>(&self, kanji_inner: &str, mut w: W)
where
W: FnMut(&str),
{
let mut block = kanji_inner.split('|');
w(block.next().unwrap());
}
fn parse_kana_part<W>(&self, kanji_inner: &str, mut w: W)
where
W: FnMut(&str),
{
let mut block = kanji_inner.split('|');
let mut pushed = false;
let kanji = block.next().unwrap();
for b in block {
if !b.is_empty() {
pushed = true;
}
w(b);
}
if self.kanji_fallback && !pushed {
w(kanji);
}
}
}
impl ToString for FuriToReadingParser<'_> {
#[inline]
fn to_string(&self) -> String {
self.parse()
}
}
impl Debug for FuriToReadingParser<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.parse())
}
}
#[cfg(test)]
mod test {
use super::FuriToReadingParser;
use test_case::test_case;
#[test_case("[音楽|おん|がく]が[好|す]き","おんがくがすき"; "parse to kana1")]
#[test_case("[2|][x|えっくす]+[1|]の[定義|てい|ぎ]が[A|えい]=[[1|],[2|]] = [[3|],[5|]]","2えっくす+1のていぎがえい=[1,2] = [3,5]"; "with brackets")]
#[test_case(
"[永遠|えい|えん]にあなたのものです。 [アーメン]",
"えいえんにあなたのものです。 [アーメン]"; "brackets"
)]
fn test_parse_to_kana(furi: &str, out: &str) {
let parsed = FuriToReadingParser::new(furi, true).parse();
assert_eq!(parsed, out);
}
#[test_case("[音楽|おん|がく]が[好|す]き","音楽が好き"; "parse to kanji1")]
#[test_case("[[3|],[5|]]ああ","[3,5]ああ"; "parse to kanji2")]
#[test_case("[2|][x|えっくす]+[1|]の[定義|てい|ぎ]が[A|えい]=[[1|],[2|]] = [[3|],[5|]]","2x+1の定義がA=[1,2] = [3,5]"; "with brackets")]
fn test_parse_to_kanji(furi: &str, out: &str) {
let parsed = FuriToReadingParser::new(furi, false).parse();
assert_eq!(parsed, out);
}
#[test]
fn test_empty_kanji_block() {
let s =
"[高校生|こう|こう|せい]の[時|とき]は[毎朝|まい|あさ][6|][時|じ]に[起|お]きていた。";
let (kana, _kanji) = FuriToReadingParser::parse_kanji_and_kana(s);
assert_eq!(kana, "こうこうせいのときはまいあさ6じにおきていた。");
}
}