use std::collections::HashMap;
use std::sync::OnceLock;
use crate::dict::PinyinDict;
type CharReadings = Vec<String>;
static REVERSE: OnceLock<HashMap<char, CharReadings>> = OnceLock::new();
fn reverse_index() -> &'static HashMap<char, CharReadings> {
REVERSE.get_or_init(|| {
let dict = PinyinDict::embedded();
let mut map: HashMap<char, CharReadings> = HashMap::new();
let raw = build_walk(&dict);
for (pinyin, word) in raw {
let mut chars = word.chars();
let (Some(first), None) = (chars.next(), chars.next()) else {
continue;
};
let readings = map.entry(first).or_default();
if !readings.contains(&pinyin) {
readings.push(pinyin);
}
}
map
})
}
fn build_walk(dict: &PinyinDict) -> Vec<(String, String)> {
let mut out = Vec::new();
out.extend(dict.prefix(""));
out
}
pub fn char_to_pinyin(c: char) -> Vec<String> {
reverse_index().get(&c).cloned().unwrap_or_default()
}
pub fn covered_char_count() -> usize {
reverse_index().len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn common_chars_have_readings() {
for (c, want) in [('我', "wo"), ('你', "ni"), ('好', "hao"), ('中', "zhong")] {
let readings = char_to_pinyin(c);
assert!(
readings.iter().any(|p| p == want),
"{c} should include reading {want:?}, got {readings:?}"
);
}
}
#[test]
fn unknown_char_yields_empty() {
assert!(char_to_pinyin('\u{E000}').is_empty());
assert!(char_to_pinyin('\u{F8FF}').is_empty());
}
#[test]
fn covered_count_reasonable() {
let n = covered_char_count();
assert!(n >= 50, "expected ≥50 single-char entries, got {n}");
}
}