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/spanish/base.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded spanish/base.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile Spanish base rules - this is a bug in liblevenshtein")
})
}
pub fn castilian() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let content = include_str!("../../../data/rules/spanish/castilian.llev");
let file = crate::phonetic::llev::parse_str(content)
.expect("Invalid embedded spanish/castilian.llev - this is a bug in liblevenshtein");
RuleSetChar::from_llev(&file)
.expect("Failed to compile Castilian rules - this is a bug in liblevenshtein")
})
}
pub fn latin_american() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let content = include_str!("../../../data/rules/spanish/latin_american.llev");
let file = crate::phonetic::llev::parse_str(content).expect(
"Invalid embedded spanish/latin_american.llev - this is a bug in liblevenshtein",
);
RuleSetChar::from_llev(&file)
.expect("Failed to compile Latin American rules - this is a bug in liblevenshtein")
})
}
pub fn combined_castilian() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let mut rules = Vec::new();
rules.extend(base().rules.iter().cloned());
rules.extend(castilian().rules.iter().cloned());
RuleSetChar {
rules,
name: Some("Combined Castilian Spanish".to_string()),
version: None,
}
})
}
pub fn combined_latin_american() -> &'static RuleSetChar {
static RULESET: OnceLock<RuleSetChar> = OnceLock::new();
RULESET.get_or_init(|| {
let mut rules = Vec::new();
rules.extend(base().rules.iter().cloned());
rules.extend(latin_american().rules.iter().cloned());
RuleSetChar {
rules,
name: Some("Combined Latin American Spanish".to_string()),
version: None,
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base_loads() {
let rules = base();
assert!(!rules.is_empty(), "Spanish base rules should not be empty");
assert!(
rules.len() > 20,
"expected >20 base rules, got {}",
rules.len()
);
}
#[test]
fn test_castilian_loads() {
let rules = castilian();
assert!(!rules.is_empty(), "Castilian rules should not be empty");
}
#[test]
fn test_latin_american_loads() {
let rules = latin_american();
assert!(
!rules.is_empty(),
"Latin American rules should not be empty"
);
}
#[test]
fn test_combined_castilian_loads() {
let rules = combined_castilian();
assert!(
!rules.is_empty(),
"Combined Castilian rules should not be empty"
);
let total = base().len() + castilian().len();
assert_eq!(
rules.len(),
total,
"combined_castilian should have all rules"
);
}
#[test]
fn test_combined_latin_american_loads() {
let rules = combined_latin_american();
assert!(
!rules.is_empty(),
"Combined Latin American rules should not be empty"
);
let total = base().len() + latin_american().len();
assert_eq!(
rules.len(),
total,
"combined_latin_american should have all rules"
);
}
#[test]
fn test_silent_h() {
let rules = base();
let result = rules.apply("hola");
assert!(!result.contains('h'), "h should be silent, got: {}", result);
}
#[test]
fn test_ll_to_y() {
let rules = base();
let result = rules.apply("llamar");
assert!(
result.starts_with('y') || result.starts_with('ʝ'),
"ll should become y or ʝ (yeísmo), got: {}",
result
);
}
#[test]
fn test_ny_from_enye() {
let rules = base();
let result = rules.apply("español");
assert!(result.contains("ɲ"), "ñ should become ny, got: {}", result);
}
#[test]
fn test_castilian_distincion() {
let rules = combined_castilian();
let caza = rules.apply("caza");
let casa = rules.apply("casa");
assert_ne!(caza, casa, "Castilian should distinguish caza from casa");
assert!(
caza.contains("θ"),
"caza should have 'th' sound, got: {}",
caza
);
}
#[test]
fn test_latin_american_seseo() {
let rules = combined_latin_american();
let caza = rules.apply("caza");
let _casa = rules.apply("casa");
assert!(
caza.contains('s'),
"caza should have 's' sound in LatAm, got: {}",
caza
);
}
#[test]
fn test_dialect_differentiation() {
let castilian_rules = combined_castilian();
let latam_rules = combined_latin_american();
let cinco_es = castilian_rules.apply("cinco");
let cinco_419 = latam_rules.apply("cinco");
assert_ne!(
cinco_es, cinco_419,
"Dialects should normalize 'cinco' differently: es='{}', es-419='{}'",
cinco_es, cinco_419
);
}
#[test]
fn test_bv_merger() {
let rules = base();
let result = rules.apply("vaca");
assert!(result.contains('b'), "v should become b, got: {}", result);
}
#[test]
fn test_j_pronunciation() {
let rules = base();
let result = rules.apply("joven");
assert!(
result.contains('x') || result.starts_with("ks"),
"j should become x (velar fricative) or ks, got: {}",
result
);
}
#[test]
fn test_g_softening() {
let rules = base();
let result = rules.apply("gente");
assert!(
result.contains('x') || result.contains("ks"),
"g before e should become x (velar fricative) or ks, got: {}",
result
);
}
#[test]
fn test_qu_handling() {
let rules = base();
let result = rules.apply("queso");
assert!(
result.starts_with('k'),
"qu should become k, got: {}",
result
);
assert!(
!result.contains('u'),
"u in qu should be silent, got: {}",
result
);
}
#[test]
fn test_ch_digraph() {
let rules = base();
let result = rules.apply("chico");
assert!(
result.contains("t͡ʃ"),
"ch should become t͡ʃ (postalveolar affricate), got: {}",
result
);
}
}