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/german/base.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded german/base.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile German 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(), "German base rules should not be empty");
assert!(
rules.len() > 40,
"expected >40 base rules, got {}",
rules.len()
);
}
#[test]
fn test_eszett() {
let rules = base();
let result = rules.apply("Straße");
assert!(
result.contains('s'),
"ß should become s (via ss→s), got: {}",
result
);
let strasse_result = rules.apply("Strasse");
assert_eq!(
result, strasse_result,
"Straße and Strasse should normalize identically"
);
}
#[test]
fn test_umlaut_a() {
let rules = base();
let result = rules.apply("Käse");
assert!(
result.contains('ɛ') || result.contains("ae"),
"ä should become ɛ, got: {}",
result
);
}
#[test]
fn test_umlaut_o() {
let rules = base();
let result = rules.apply("schön");
assert!(
result.contains('ø') || result.contains("oe"),
"ö should become ø, got: {}",
result
);
}
#[test]
fn test_umlaut_u() {
let rules = base();
let result = rules.apply("München");
assert!(
result.contains('y') || result.contains("ue"),
"ü should become y, got: {}",
result
);
}
#[test]
fn test_sch_digraph() {
let rules = base();
let result = rules.apply("Schule");
assert!(
result.starts_with("ʃ"),
"sch should become sh, got: {}",
result
);
}
#[test]
fn test_final_devoicing_d() {
let rules = base();
let result = rules.apply("Hund");
assert!(
result.ends_with('t'),
"final d should become t, got: {}",
result
);
}
#[test]
fn test_final_devoicing_b() {
let rules = base();
let result = rules.apply("gelb");
assert!(
result.ends_with('p'),
"final b should become p, got: {}",
result
);
}
#[test]
fn test_final_devoicing_g() {
let rules = base();
let result = rules.apply("Tag");
assert!(
result.ends_with('k'),
"final g should become k, got: {}",
result
);
}
#[test]
fn test_w_pronunciation() {
let rules = base();
let result = rules.apply("Wasser");
assert!(
result.starts_with('v') || result.starts_with('V') || result.starts_with('f'),
"W should become v or f, got: {}",
result
);
}
#[test]
fn test_z_pronunciation() {
let rules = base();
let result = rules.apply("Zeit");
assert!(
result.starts_with("t͡s"),
"z should become ts, got: {}",
result
);
}
#[test]
fn test_sp_initial() {
let rules = base();
let result = rules.apply("Spiel");
assert!(
result.starts_with("ʃp") || result.starts_with("shp"),
"initial sp should become ʃp, got: {}",
result
);
}
#[test]
fn test_st_initial() {
let rules = base();
let result = rules.apply("Stein");
assert!(
result.starts_with("ʃt") || result.starts_with("sht"),
"initial st should become ʃt, got: {}",
result
);
}
#[test]
fn test_ie_vowel() {
let rules = base();
let result = rules.apply("Liebe");
assert!(
result.contains('i') && !result.contains("ie"),
"ie should become i, got: {}",
result
);
}
#[test]
fn test_ei_diphthong() {
let rules = base();
let result = rules.apply("Eis");
assert!(
result.contains("aɪ"),
"ei should become AI (uppercase), got: {}",
result
);
}
#[test]
fn test_eu_diphthong() {
let rules = base();
let result = rules.apply("neu");
assert!(
result.contains("ɔʏ"),
"eu should become OY (uppercase), got: {}",
result
);
}
#[test]
fn test_au_diphthong() {
let rules = base();
let result = rules.apply("Haus");
assert!(
result.contains("aʊ"),
"au should become AU (uppercase), got: {}",
result
);
}
#[test]
fn test_qu_pattern() {
let rules = base();
let result = rules.apply("Quelle");
assert!(
result.starts_with("kv") || result.starts_with("kV") || result.starts_with("kf"),
"qu should become kv or kf, got: {}",
result
);
}
#[test]
fn test_ph_pattern() {
let rules = base();
let result = rules.apply("Philosophie");
assert!(result.contains('f'), "ph should become f, got: {}", result);
assert!(
!result.contains("ph"),
"ph should not remain, got: {}",
result
);
}
#[test]
fn test_silent_h() {
let rules = base();
let result = rules.apply("Sahne");
assert!(
!result.contains("ah"),
"ah should become a (silent h), got: {}",
result
);
}
#[test]
fn test_ck_digraph() {
let rules = base();
let result = rules.apply("Stück");
assert!(
!result.contains("ck"),
"ck should become k, got: {}",
result
);
}
#[test]
fn test_double_consonants() {
let rules = base();
let result = rules.apply("Mutter");
assert!(
!result.contains("tt"),
"tt should become t, got: {}",
result
);
}
}