use wasm_bindgen::prelude::*;
use golia_pinyin::{L0Snapshot, PinyinDict, char_to_pinyin};
#[wasm_bindgen]
pub struct PinyinEngine {
dict: PinyinDict,
}
#[wasm_bindgen]
impl PinyinEngine {
#[wasm_bindgen(constructor)]
pub fn new() -> PinyinEngine {
Self {
dict: PinyinDict::embedded(),
}
}
#[wasm_bindgen(getter)]
pub fn len(&self) -> usize {
self.dict.len()
}
#[wasm_bindgen(getter, js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
self.dict.is_empty()
}
pub fn lookup(&self, pinyin: &str) -> js_sys::Array {
let arr = js_sys::Array::new();
for word in self.dict.lookup(pinyin) {
arr.push(&JsValue::from_str(&word));
}
arr
}
#[wasm_bindgen(js_name = prefix)]
pub fn prefix_lookup(&self, prefix: &str) -> js_sys::Array {
let arr = js_sys::Array::new();
for (pinyin, word) in self.dict.prefix(prefix) {
let obj = js_sys::Object::new();
let _ = js_sys::Reflect::set(&obj, &"pinyin".into(), &JsValue::from_str(&pinyin));
let _ = js_sys::Reflect::set(&obj, &"word".into(), &JsValue::from_str(&word));
arr.push(&obj);
}
arr
}
pub fn encode(&self, ch: &str) -> js_sys::Array {
let arr = js_sys::Array::new();
let Some(c) = ch.chars().next() else {
return arr;
};
for r in char_to_pinyin(c) {
arr.push(&JsValue::from_str(&r));
}
arr
}
#[wasm_bindgen(js_name = recordPick)]
pub fn record_pick(&self, pinyin: &str, word: &str) -> bool {
self.dict.record_pick(pinyin, word)
}
pub fn pin(&self, pinyin: &str, word: &str) -> bool {
self.dict.pin(pinyin, word)
}
pub fn forget(&self, pinyin: &str) -> bool {
self.dict.forget(pinyin)
}
#[wasm_bindgen(js_name = l0PinCount, getter)]
pub fn l0_pin_count(&self) -> usize {
self.dict.l0_pin_count()
}
#[wasm_bindgen(js_name = l0PendingCount, getter)]
pub fn l0_pending_count(&self) -> usize {
self.dict.l0_pending_count()
}
#[wasm_bindgen(js_name = exportL0)]
pub fn export_l0(&self) -> JsValue {
let snap = self.dict.export_l0();
let obj = js_sys::Object::new();
let pins = js_sys::Array::new();
for (p, w) in &snap.pins {
let pair = js_sys::Array::new();
pair.push(&JsValue::from_str(p));
pair.push(&JsValue::from_str(w));
pins.push(&pair);
}
let _ = js_sys::Reflect::set(&obj, &"pins".into(), &pins);
let counts = js_sys::Array::new();
for (p, w, n) in &snap.pick_counts {
let triple = js_sys::Array::new();
triple.push(&JsValue::from_str(p));
triple.push(&JsValue::from_str(w));
triple.push(&JsValue::from_f64(*n as f64));
counts.push(&triple);
}
let _ = js_sys::Reflect::set(&obj, &"pickCounts".into(), &counts);
obj.into()
}
#[wasm_bindgen(js_name = importL0)]
pub fn import_l0(&self, state: JsValue) -> usize {
let Some(obj) = state.dyn_ref::<js_sys::Object>() else {
return 0;
};
let snap = parse_snapshot(obj);
self.dict.import_l0(snap)
}
}
impl Default for PinyinEngine {
fn default() -> Self {
Self::new()
}
}
fn parse_snapshot(obj: &js_sys::Object) -> L0Snapshot {
let pins = js_sys::Reflect::get(obj, &"pins".into())
.ok()
.and_then(|v| v.dyn_into::<js_sys::Array>().ok())
.map(|arr| {
arr.iter()
.filter_map(|pair| {
let pair: js_sys::Array = pair.dyn_into().ok()?;
let p = pair.get(0).as_string()?;
let w = pair.get(1).as_string()?;
Some((p, w))
})
.collect()
})
.unwrap_or_default();
let pick_counts = js_sys::Reflect::get(obj, &"pickCounts".into())
.ok()
.and_then(|v| v.dyn_into::<js_sys::Array>().ok())
.map(|arr| {
arr.iter()
.filter_map(|triple| {
let triple: js_sys::Array = triple.dyn_into().ok()?;
let p = triple.get(0).as_string()?;
let w = triple.get(1).as_string()?;
let n = triple.get(2).as_f64()? as u32;
Some((p, w, n))
})
.collect()
})
.unwrap_or_default();
L0Snapshot { pins, pick_counts }
}