use crate::phonetic::llev::RuleSetChar;
use std::sync::OnceLock;
pub fn base() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let content = include_str!("../../../data/rules/vietnamese/base.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded vietnamese/base.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile Vietnamese base rules - this is a bug in liblevenshtein")
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base_loads() {
let rules = base();
assert!(
!rules.is_empty(),
"Vietnamese base rules should not be empty"
);
assert!(
rules.len() >= 100,
"expected >=100 base rules, got {}",
rules.len()
);
}
#[test]
fn test_d_stroke() {
let rules = base();
let result = rules.apply("đ");
assert!(result.contains('d'), "đ should become d, got: {}", result);
}
#[test]
fn test_ngh_digraph() {
let rules = base();
let result = rules.apply("ngh");
assert!(
result.contains("ŋ"),
"ngh should become ng, got: {}",
result
);
}
#[test]
fn test_ph_digraph() {
let rules = base();
let result = rules.apply("ph");
assert!(result.contains('f'), "ph should become f, got: {}", result);
}
#[test]
fn test_nh_digraph() {
let rules = base();
let result = rules.apply("nh");
assert!(result.contains("ɲ"), "nh should become ny, got: {}", result);
}
#[test]
fn test_gi_digraph() {
let rules = base();
let result = rules.apply("gi");
assert!(result.contains('z'), "gi should become z, got: {}", result);
}
#[test]
fn test_qu_digraph() {
let rules = base();
let result = rules.apply("qu");
assert!(
result.contains("kw"),
"qu should become kw, got: {}",
result
);
}
#[test]
fn test_a_grave() {
let rules = base();
let result = rules.apply("à");
assert!(result.contains('a'), "à should become a, got: {}", result);
}
#[test]
fn test_a_acute() {
let rules = base();
let result = rules.apply("á");
assert!(result.contains('a'), "á should become a, got: {}", result);
}
#[test]
fn test_a_hook() {
let rules = base();
let result = rules.apply("ả");
assert!(result.contains('a'), "ả should become a, got: {}", result);
}
#[test]
fn test_a_tilde() {
let rules = base();
let result = rules.apply("ã");
assert!(result.contains('a'), "ã should become a, got: {}", result);
}
#[test]
fn test_a_dot_below() {
let rules = base();
let result = rules.apply("ạ");
assert!(result.contains('a'), "ạ should become a, got: {}", result);
}
#[test]
fn test_a_breve() {
let rules = base();
let result = rules.apply("ă");
assert!(result.contains('a'), "ă should become a, got: {}", result);
}
#[test]
fn test_a_circumflex() {
let rules = base();
let result = rules.apply("â");
assert!(result.contains('a'), "â should become a, got: {}", result);
}
#[test]
fn test_e_circumflex() {
let rules = base();
let result = rules.apply("ê");
assert!(result.contains('e'), "ê should become e, got: {}", result);
}
#[test]
fn test_o_circumflex() {
let rules = base();
let result = rules.apply("ô");
assert!(
result.contains('o') || result.contains('ɔ'),
"ô should become o, got: {}",
result
);
}
#[test]
fn test_o_horn() {
let rules = base();
let result = rules.apply("ơ");
assert!(
result.contains('o') || result.contains('ɔ'),
"ơ should become o, got: {}",
result
);
}
#[test]
fn test_u_horn() {
let rules = base();
let result = rules.apply("ư");
assert!(result.contains('u'), "ư should become u, got: {}", result);
}
#[test]
fn test_a_breve_with_tone() {
let rules = base();
let result = rules.apply("ắ");
assert!(result.contains('a'), "ắ should become a, got: {}", result);
}
#[test]
fn test_o_horn_with_tone() {
let rules = base();
let result = rules.apply("ớ");
assert!(
result.contains('o') || result.contains('ɔ'),
"ớ should become o, got: {}",
result
);
}
#[test]
fn test_u_horn_with_tone() {
let rules = base();
let result = rules.apply("ứ");
assert!(result.contains('u'), "ứ should become u, got: {}", result);
}
#[test]
fn test_word_vietnam() {
let rules = base();
let result = rules.apply_full("Việt Nam");
let lower = result.to_lowercase();
assert!(
lower.contains("viet") && lower.contains("nam"),
"Việt Nam should normalize to contain viet and nam, got: {}",
result
);
}
#[test]
fn test_word_hanoi() {
let rules = base();
let result = rules.apply_full("Hà Nội");
let lower = result.to_lowercase();
assert!(
lower.contains("ha") && lower.contains("noi"),
"Hà Nội should normalize to contain ha and noi, got: {}",
result
);
}
#[test]
fn test_word_pho() {
let rules = base();
let result = rules.apply_full("Phở");
let lower = result.to_lowercase();
assert!(
lower.contains('f') && (lower.contains('o') || lower.contains('ɔ')),
"Phở should normalize to contain f and o, got: {}",
result
);
}
#[test]
fn test_word_nguyen() {
let rules = base();
let result = rules.apply_full("Nguyễn");
let lower = result.to_lowercase();
assert!(
lower.contains("ŋ")
&& lower.contains('u')
&& lower.contains('y')
&& lower.contains('e')
&& lower.contains('n'),
"Nguyễn should normalize to nguyen, got: {}",
result
);
}
}