Skip to main content

wowlab_parsers/parsers/spell_desc/
wasm.rs

1use wasm_bindgen::prelude::*;
2use wowlab_types::{
3    spell_desc::SpellDescDependencies, spell_render::SpellRenderInput, wasm::WasmCommonError,
4};
5
6use super::{
7    analyzer::analyze_dependencies, game_data_resolver::GameDataResolver,
8    lexer::tokenize_to_fragments, parser::parse, renderer::render_with_resolver,
9};
10
11/// WASM-exposed result of analyzing a spell description: dependencies + parse errors.
12#[derive(Debug)]
13#[wasm_bindgen(js_name = AnalyzeResult)]
14pub struct WasmAnalyzeResult {
15    dependencies: SpellDescDependencies,
16    parse_errors: Vec<Box<str>>,
17}
18
19#[wasm_bindgen(js_class = AnalyzeResult)]
20impl WasmAnalyzeResult {
21    #[wasm_bindgen(getter)]
22    #[must_use]
23    pub fn dependencies(&self) -> JsValue {
24        serde_wasm_bindgen::to_value(&self.dependencies).unwrap_or(JsValue::NULL)
25    }
26
27    #[wasm_bindgen(getter, js_name = parseErrors)]
28    pub fn parse_errors(&self) -> Vec<String> {
29        self.parse_errors.iter().map(ToString::to_string).collect()
30    }
31
32    #[wasm_bindgen(js_name = hasErrors)]
33    #[must_use]
34    pub fn has_errors(&self) -> bool {
35        !self.parse_errors.is_empty()
36    }
37}
38
39/// Analyze a spell description and return its data dependencies.
40#[wasm_bindgen(js_name = analyzeSpellDesc)]
41#[must_use]
42pub fn wasm_analyze_spell_desc(input: &str, self_spell_id: u32) -> WasmAnalyzeResult {
43    let result = parse(input);
44    let dependencies = analyze_dependencies(&result.ast, self_spell_id);
45
46    WasmAnalyzeResult {
47        dependencies,
48        parse_errors: result
49            .errors
50            .iter()
51            .map(|error| error.to_string().into_boxed_str())
52            .collect(),
53    }
54}
55
56/// Render a spell description against a resolved character bundle into structured fragments.
57///
58/// # Errors
59///
60/// Returns [`WasmCommonError`] when the rendered result cannot be serialized for JavaScript.
61#[wasm_bindgen(js_name = renderSpellDescWithData)]
62#[expect(
63    clippy::needless_pass_by_value,
64    reason = "Tsify deserializes this JavaScript value into an owned Rust input at the ABI boundary"
65)]
66pub fn wasm_render_spell_desc_with_data(
67    input: SpellRenderInput,
68) -> Result<JsValue, WasmCommonError> {
69    let resolver = GameDataResolver::new(&input);
70    let result = parse(&input.self_spell.description);
71    let parse_errors: Vec<String> = result.errors.iter().map(ToString::to_string).collect();
72    let render_result =
73        render_with_resolver(&result.ast, input.self_spell.id, &resolver, parse_errors);
74
75    serde_wasm_bindgen::to_value(&render_result)
76        .map_err(|e| WasmCommonError::Serialize(e.to_string()))
77}
78
79/// Tokenize a spell description into fragments for debug display.
80///
81/// # Errors
82///
83/// Returns [`WasmCommonError`] when the token fragments cannot be serialized for JavaScript.
84#[wasm_bindgen(js_name = tokenizeSpellDesc)]
85pub fn wasm_tokenize_spell_desc(input: &str) -> Result<JsValue, WasmCommonError> {
86    let fragments = tokenize_to_fragments(input);
87
88    Ok(serde_wasm_bindgen::to_value(&fragments)?)
89}