use crate::Shlesha;
use js_sys::{Array, Object, Reflect};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen(start)]
pub fn main() {
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
pub struct WasmShlesha {
inner: Shlesha,
}
#[wasm_bindgen]
pub struct WasmUnknownToken {
script: String,
token: String,
position: usize,
unicode: String,
is_extension: bool,
}
#[wasm_bindgen]
pub struct WasmTransliterationMetadata {
source_script: String,
target_script: String,
unknown_tokens: Vec<WasmUnknownToken>,
}
#[wasm_bindgen]
pub struct WasmTransliterationResult {
output: String,
metadata: Option<WasmTransliterationMetadata>,
}
#[wasm_bindgen]
impl WasmShlesha {
#[wasm_bindgen(constructor)]
#[allow(clippy::new_without_default)]
pub fn new() -> WasmShlesha {
WasmShlesha {
inner: Shlesha::new(),
}
}
#[wasm_bindgen]
pub fn transliterate(
&self,
text: &str,
from_script: &str,
to_script: &str,
) -> Result<String, JsValue> {
self.inner
.transliterate(text, from_script, to_script)
.map_err(|e| JsValue::from_str(&format!("Transliteration failed: {e}")))
}
#[wasm_bindgen(js_name = transliterateWithMetadata)]
pub fn transliterate_with_metadata(
&self,
text: &str,
from_script: &str,
to_script: &str,
) -> Result<WasmTransliterationResult, JsValue> {
let result = self
.inner
.transliterate_with_metadata(text, from_script, to_script)
.map_err(|e| JsValue::from_str(&format!("Transliteration failed: {e}")))?;
let wasm_metadata = result.metadata.map(|metadata| {
let unknown_tokens = metadata
.unknown_tokens
.into_iter()
.map(|token| WasmUnknownToken {
script: token.script,
token: token.token.to_string(),
position: token.position,
unicode: token.unicode,
is_extension: token.is_extension,
})
.collect();
WasmTransliterationMetadata {
source_script: metadata.source_script,
target_script: metadata.target_script,
unknown_tokens,
}
});
Ok(WasmTransliterationResult {
output: result.output,
metadata: wasm_metadata,
})
}
#[wasm_bindgen(js_name = listSupportedScripts)]
pub fn list_supported_scripts(&self) -> Array {
let scripts = self.inner.list_supported_scripts();
let array = Array::new();
for script in scripts {
array.push(&JsValue::from_str(&script));
}
array
}
#[wasm_bindgen(js_name = supportsScript)]
pub fn supports_script(&self, script: &str) -> bool {
self.inner.supports_script(script)
}
#[wasm_bindgen(js_name = loadSchema)]
pub fn load_schema(&mut self, schema_path: &str) -> Result<(), JsValue> {
self.inner
.load_schema_from_file(schema_path)
.map_err(|e| JsValue::from_str(&format!("Schema loading failed: {e}")))
}
#[wasm_bindgen(js_name = getScriptInfo)]
pub fn get_script_info(&self) -> Result<JsValue, JsValue> {
let obj = Object::new();
for script in self.inner.list_supported_scripts() {
let description = match script.as_str() {
"iast" => "IAST (International Alphabet of Sanskrit Transliteration)",
"itrans" => "ITRANS (ASCII transliteration)",
"slp1" => "SLP1 (Sanskrit Library Phonetic scheme)",
"harvard_kyoto" | "hk" => "Harvard-Kyoto (ASCII-based academic standard)",
"velthuis" => "Velthuis (TeX-based notation)",
"wx" => "WX (Computational notation)",
"devanagari" | "deva" => "Devanagari script (देवनागरी)",
"bengali" | "bn" => "Bengali script (বাংলা)",
"tamil" | "ta" => "Tamil script (தமிழ்)",
"telugu" | "te" => "Telugu script (తెలుగు)",
"gujarati" | "gu" => "Gujarati script (ગુજરાતી)",
"kannada" | "kn" => "Kannada script (ಕನ್ನಡ)",
"malayalam" | "ml" => "Malayalam script (മലയാളം)",
"odia" | "od" | "oriya" => "Odia script (ଓଡ଼ିଆ)",
"iso15919" | "iso" | "iso_15919" => "ISO-15919 (International standard)",
_ => "Unknown script type",
};
Reflect::set(
&obj,
&JsValue::from_str(&script),
&JsValue::from_str(description),
)
.map_err(|_| JsValue::from_str("Failed to set property"))?;
}
Ok(obj.into())
}
#[wasm_bindgen(js_name = getSupportedScriptCount)]
pub fn get_supported_script_count(&self) -> usize {
self.inner.list_supported_scripts().len()
}
#[wasm_bindgen(js_name = loadSchemaFromFile)]
pub fn load_schema_from_file(&mut self, file_path: &str) -> Result<(), JsValue> {
self.inner
.load_schema_from_file(file_path)
.map_err(|e| JsValue::from_str(&format!("Schema loading failed: {e}")))
}
#[wasm_bindgen(js_name = loadSchemaFromString)]
pub fn load_schema_from_string(
&mut self,
yaml_content: &str,
schema_name: &str,
) -> Result<(), JsValue> {
self.inner
.load_schema_from_string(yaml_content, schema_name)
.map_err(|e| JsValue::from_str(&format!("Schema loading failed: {e}")))
}
#[wasm_bindgen(js_name = getSchemaInfo)]
pub fn get_schema_info(&self, script_name: &str) -> Option<Object> {
self.inner.get_schema_info(script_name).map(|info| {
let obj = Object::new();
let _ = Reflect::set(&obj, &"name".into(), &JsValue::from_str(&info.name));
let _ = Reflect::set(
&obj,
&"description".into(),
&JsValue::from_str(&info.description),
);
let _ = Reflect::set(
&obj,
&"script_type".into(),
&JsValue::from_str(&info.script_type),
);
let _ = Reflect::set(
&obj,
&"is_runtime_loaded".into(),
&JsValue::from_bool(info.is_runtime_loaded),
);
let _ = Reflect::set(
&obj,
&"mapping_count".into(),
&JsValue::from_f64(info.mapping_count as f64),
);
obj
})
}
#[wasm_bindgen(js_name = removeSchema)]
pub fn remove_schema(&mut self, script_name: &str) -> bool {
self.inner.remove_schema(script_name)
}
#[wasm_bindgen(js_name = clearRuntimeSchemas)]
pub fn clear_runtime_schemas(&mut self) {
self.inner.clear_runtime_schemas()
}
}
#[wasm_bindgen]
impl WasmTransliterationResult {
#[wasm_bindgen(js_name = getOutput)]
pub fn get_output(&self) -> String {
self.output.clone()
}
#[wasm_bindgen(js_name = hasMetadata)]
pub fn has_metadata(&self) -> bool {
self.metadata.is_some()
}
#[wasm_bindgen(js_name = getSourceScript)]
pub fn get_source_script(&self) -> Option<String> {
self.metadata.as_ref().map(|m| m.source_script.clone())
}
#[wasm_bindgen(js_name = getTargetScript)]
pub fn get_target_script(&self) -> Option<String> {
self.metadata.as_ref().map(|m| m.target_script.clone())
}
#[wasm_bindgen(js_name = getUnknownTokenCount)]
pub fn get_unknown_token_count(&self) -> usize {
self.metadata
.as_ref()
.map(|m| m.unknown_tokens.len())
.unwrap_or(0)
}
#[wasm_bindgen(js_name = getUnknownTokens)]
pub fn get_unknown_tokens(&self) -> Result<Array, JsValue> {
let array = Array::new();
if let Some(metadata) = &self.metadata {
for token in &metadata.unknown_tokens {
let obj = Object::new();
Reflect::set(
&obj,
&JsValue::from_str("script"),
&JsValue::from_str(&token.script),
)?;
Reflect::set(
&obj,
&JsValue::from_str("token"),
&JsValue::from_str(&token.token),
)?;
Reflect::set(
&obj,
&JsValue::from_str("position"),
&JsValue::from_f64(token.position as f64),
)?;
Reflect::set(
&obj,
&JsValue::from_str("unicode"),
&JsValue::from_str(&token.unicode),
)?;
Reflect::set(
&obj,
&JsValue::from_str("isExtension"),
&JsValue::from_bool(token.is_extension),
)?;
array.push(&obj);
}
}
Ok(array)
}
}
#[wasm_bindgen(js_name = createTransliterator)]
pub fn create_transliterator() -> WasmShlesha {
WasmShlesha::new()
}
#[wasm_bindgen]
pub fn transliterate(text: &str, from_script: &str, to_script: &str) -> Result<String, JsValue> {
let transliterator = Shlesha::new();
transliterator
.transliterate(text, from_script, to_script)
.map_err(|e| JsValue::from_str(&format!("Transliteration failed: {e}")))
}
#[wasm_bindgen(js_name = getSupportedScripts)]
pub fn get_supported_scripts() -> Array {
let transliterator = Shlesha::new();
let scripts = transliterator.list_supported_scripts();
let array = Array::new();
for script in scripts {
array.push(&JsValue::from_str(&script));
}
array
}
#[wasm_bindgen(js_name = getVersion)]
pub fn get_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_wasm_basic_transliteration() {
let transliterator = WasmShlesha::new();
let result = transliterator
.transliterate("अ", "devanagari", "iast")
.unwrap();
assert_eq!(result, "a");
}
#[wasm_bindgen_test]
fn test_wasm_metadata_collection() {
let transliterator = WasmShlesha::new();
let result = transliterator
.transliterate_with_metadata("धर्मkr", "devanagari", "iast")
.unwrap();
assert!(result.get_output().contains("dharma"));
assert!(result.has_metadata());
let _count = result.get_unknown_token_count();
}
#[wasm_bindgen_test]
fn test_wasm_script_support() {
let transliterator = WasmShlesha::new();
assert!(transliterator.supports_script("devanagari"));
assert!(transliterator.supports_script("iast"));
assert!(!transliterator.supports_script("nonexistent"));
let scripts = transliterator.list_supported_scripts();
assert!(scripts.length() > 0);
}
#[wasm_bindgen_test]
fn test_wasm_convenience_functions() {
let result = transliterate("अ", "devanagari", "iast").unwrap();
assert_eq!(result, "a");
let scripts = get_supported_scripts();
assert!(scripts.length() > 0);
let version = get_version();
assert!(!version.is_empty());
}
#[wasm_bindgen_test]
fn test_wasm_cross_script_conversion() {
let transliterator = WasmShlesha::new();
let result = transliterator
.transliterate("धर्म", "devanagari", "gujarati")
.unwrap();
assert!(!result.is_empty());
assert!(result.contains("ધ") || result.contains("गुज")); }
#[wasm_bindgen_test]
fn test_wasm_script_info() {
let transliterator = WasmShlesha::new();
let info = transliterator.get_script_info().unwrap();
let obj: &js_sys::Object = info.unchecked_ref();
assert!(js_sys::Object::keys(obj).length() > 0);
}
#[wasm_bindgen_test]
fn test_wasm_error_handling() {
let transliterator = WasmShlesha::new();
let result = transliterator.transliterate("test", "invalid_script", "iast");
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_wasm_empty_input() {
let transliterator = WasmShlesha::new();
let result = transliterator
.transliterate("", "devanagari", "iast")
.unwrap();
assert_eq!(result, "");
}
#[wasm_bindgen_test]
fn test_wasm_whitespace_preservation() {
let transliterator = WasmShlesha::new();
let result = transliterator
.transliterate("अ आ", "devanagari", "iast")
.unwrap();
assert!(result.contains(" "));
}
}