use crate::phonetic::llev::RuleSetChar;
use std::sync::OnceLock;
pub fn pinyin() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let content = include_str!("../../../data/rules/chinese/pinyin.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded chinese/pinyin.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile Chinese pinyin rules - this is a bug in liblevenshtein")
})
}
pub fn characters() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let content = include_str!("../../../data/rules/chinese/characters.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded chinese/characters.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile Chinese character rules - this is a bug in liblevenshtein")
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pinyin_loads() {
let rules = pinyin();
assert!(
!rules.is_empty(),
"Chinese pinyin rules should not be empty"
);
assert!(
rules.len() > 35,
"expected >35 pinyin rules, got {}",
rules.len()
);
}
#[test]
fn test_tone_mark_first() {
let rules = pinyin();
let result = rules.apply("ā");
assert!(result.contains('a'), "ā should become a, got: {}", result);
let result = rules.apply("ē");
assert!(result.contains('e'), "ē should become e, got: {}", result);
}
#[test]
fn test_tone_mark_second() {
let rules = pinyin();
let result = rules.apply("á");
assert!(result.contains('a'), "á should become a, got: {}", result);
let result = rules.apply("é");
assert!(result.contains('e'), "é should become e, got: {}", result);
}
#[test]
fn test_tone_mark_third() {
let rules = pinyin();
let result = rules.apply("ǎ");
assert!(result.contains('a'), "ǎ should become a, got: {}", result);
let result = rules.apply("ě");
assert!(result.contains('e'), "ě should become e, got: {}", result);
}
#[test]
fn test_tone_mark_fourth() {
let rules = pinyin();
let result = rules.apply("à");
assert!(result.contains('a'), "à should become a, got: {}", result);
let result = rules.apply("è");
assert!(result.contains('e'), "è should become e, got: {}", result);
}
#[test]
fn test_u_umlaut() {
let rules = pinyin();
let result = rules.apply("ü");
assert!(
result.contains('y') || result.contains('U'),
"ü should become y, got: {}",
result
);
let result = rules.apply("v");
assert!(
result.contains('y') || result.contains('U'),
"v should become y, got: {}",
result
);
}
#[test]
fn test_retroflex_consonants() {
let rules = pinyin();
let result = rules.apply("zh");
assert!(
result.contains("ʈ͡ʂ") || result.contains('Z'),
"zh should become ʈ͡ʂ, got: {}",
result
);
let result = rules.apply("ch");
assert!(
result.contains("ʈ͡ʂ") || result.contains('C'),
"ch should become ʈ͡ʂʰ, got: {}",
result
);
let result = rules.apply("sh");
assert!(
result.contains('ʂ') || result.contains('ʃ') || result.contains('S'),
"sh should become ʂ, got: {}",
result
);
let result = rules.apply("r");
assert!(
result.contains('ɻ') || result.contains('R'),
"r should become ɻ, got: {}",
result
);
}
#[test]
fn test_palatal_consonants() {
let rules = pinyin();
let result = rules.apply("x");
assert!(
result.contains('ɕ') || result.contains('X'),
"x should become ɕ, got: {}",
result
);
let result = rules.apply("q");
assert!(
result.contains("t͡ɕ") || result.contains("tɕ") || result.contains('Q'),
"q should become t͡ɕʰ, got: {}",
result
);
}
#[test]
fn test_affricate_c() {
let rules = pinyin();
let result = rules.apply("c");
assert!(result.contains("t͡s"), "c should become TS, got: {}", result);
}
#[test]
fn test_word_nihao() {
let rules = pinyin();
let result = rules.apply("nǐhǎo");
assert!(
result.contains('n')
&& result.contains('i')
&& result.contains('h')
&& result.contains('a')
&& result.contains('o'),
"nǐhǎo should normalize to nihao-like, got: {}",
result
);
}
#[test]
fn test_word_zhongguo() {
let rules = pinyin();
let result = rules.apply("zhōngguó");
assert!(
(result.contains("ʈ͡ʂ") || result.contains('Z'))
&& (result.contains('o') || result.contains('ɔ')),
"zhōngguó should have ʈ͡ʂ (from zh), got: {}",
result
);
}
#[test]
fn test_word_nv() {
let rules = pinyin();
let result = rules.apply("nv");
assert!(
result.contains('n') && (result.contains('y') || result.contains('U')),
"nv should become ny, got: {}",
result
);
}
#[test]
fn test_characters_loads() {
let rules = characters();
assert!(
!rules.is_empty(),
"Chinese character rules should not be empty"
);
assert!(
rules.len() > 350,
"expected >350 character rules, got {}",
rules.len()
);
}
#[test]
fn test_character_pronouns() {
let rules = characters();
let result = rules.apply("我");
assert!(
result.contains("wo"),
"我 should become wo, got: {}",
result
);
let result = rules.apply("你");
assert!(
result.contains("ni"),
"你 should become ni, got: {}",
result
);
let result = rules.apply("他");
assert!(
result.contains("ta"),
"他 should become ta, got: {}",
result
);
let result = rules.apply("她");
assert!(
result.contains("ta"),
"她 should become ta, got: {}",
result
);
}
#[test]
fn test_character_numbers() {
let rules = characters();
let result = rules.apply("一");
assert!(
result.contains("yi"),
"一 should become yi, got: {}",
result
);
let result = rules.apply("二");
assert!(
result.contains("er"),
"二 should become er, got: {}",
result
);
let result = rules.apply("三");
assert!(
result.contains("san"),
"三 should become san, got: {}",
result
);
let result = rules.apply("十");
assert!(
result.contains("shi"),
"十 should become shi, got: {}",
result
);
}
#[test]
fn test_character_common_verbs() {
let rules = characters();
let result = rules.apply("是");
assert!(
result.contains("shi"),
"是 should become shi, got: {}",
result
);
let result = rules.apply("有");
assert!(
result.contains("you"),
"有 should become you, got: {}",
result
);
let result = rules.apply("在");
assert!(
result.contains("zai"),
"在 should become zai, got: {}",
result
);
let result = rules.apply("去");
assert!(
result.contains("qu"),
"去 should become qu, got: {}",
result
);
}
#[test]
fn test_character_common_nouns() {
let rules = characters();
let result = rules.apply("人");
assert!(
result.contains("ren"),
"人 should become ren, got: {}",
result
);
let result = rules.apply("家");
assert!(
result.contains("jia"),
"家 should become jia, got: {}",
result
);
let result = rules.apply("学");
assert!(
result.contains("xue"),
"学 should become xue, got: {}",
result
);
}
#[test]
fn test_character_multi_char_compound() {
let rules = characters();
let result = rules.apply("我们");
assert!(
result.contains("women") || result.contains("wo"),
"我们 should contain wo or women, got: {}",
result
);
let result = rules.apply("你们");
assert!(
result.contains("nimen") || result.contains("ni"),
"你们 should contain ni or nimen, got: {}",
result
);
}
#[test]
fn test_character_sentence() {
let rules = characters();
let result = rules.apply("你好");
assert!(
result.contains("ni") && result.contains("hao"),
"你好 should contain ni and hao, got: {}",
result
);
}
#[test]
fn test_character_china() {
let rules = characters();
let result = rules.apply("中国");
assert!(
result.contains("zhong") && result.contains("guo"),
"中国 should contain zhong and guo, got: {}",
result
);
}
#[test]
fn test_character_person() {
let rules = characters();
let result = rules.apply("中国人");
assert!(
result.contains("ren"),
"中国人 should contain ren, got: {}",
result
);
}
}