Skip to main content

busbar_sf_agentscript/
wasm.rs

1//! WebAssembly bindings for the AgentScript parser.
2//!
3//! This module provides JavaScript-accessible functions for parsing
4//! AgentScript source code and working with the resulting AST.
5//!
6//! # Example (JavaScript)
7//!
8//! ```javascript
9//! import init, { parse_agent, parse_agent_to_json, serialize_agent } from './sf_agentscript.js';
10//!
11//! await init();
12//!
13//! // Parse to JS object
14//! const ast = parse_agent(source);
15//! console.log(ast.config.agent_name);
16//!
17//! // Or parse to JSON string
18//! const json = parse_agent_to_json(source);
19//!
20//! // Serialize AST back to source
21//! const regenerated = serialize_agent(ast);
22//! ```
23
24use crate::validation::Severity;
25use serde::Serialize;
26use wasm_bindgen::prelude::*;
27
28#[derive(Serialize)]
29struct ValidationReport {
30    errors: Vec<crate::validation::SemanticError>,
31    warnings: Vec<crate::validation::SemanticError>,
32}
33
34/// Initialize panic hook for better error messages in the browser console.
35#[wasm_bindgen(start)]
36pub fn init() {
37    #[cfg(feature = "wasm")]
38    console_error_panic_hook::set_once();
39}
40
41/// Parse AgentScript source code and return the AST as a JavaScript object.
42///
43/// # Arguments
44/// * `source` - The AgentScript source code to parse
45///
46/// # Returns
47/// * `Ok(JsValue)` - The parsed AST as a JavaScript object
48/// * `Err(JsValue)` - Error message if parsing fails
49#[wasm_bindgen]
50pub fn parse_agent(source: &str) -> Result<JsValue, JsValue> {
51    match crate::parse(source) {
52        Ok(ast) => serde_wasm_bindgen::to_value(&ast)
53            .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
54        Err(errs) => Err(JsValue::from_str(&errs.join("\n"))),
55    }
56}
57
58/// Parse AgentScript source code and return the AST as a JSON string.
59///
60/// This is useful when you need to pass the AST to another system
61/// or want to inspect it as text.
62///
63/// # Arguments
64/// * `source` - The AgentScript source code to parse
65///
66/// # Returns
67/// * `Ok(String)` - The parsed AST as a JSON string
68/// * `Err(JsValue)` - Error message if parsing fails
69#[wasm_bindgen]
70pub fn parse_agent_to_json(source: &str) -> Result<String, JsValue> {
71    match crate::parse(source) {
72        Ok(ast) => serde_json::to_string_pretty(&ast)
73            .map_err(|e| JsValue::from_str(&format!("JSON serialization error: {}", e))),
74        Err(errs) => Err(JsValue::from_str(&errs.join("\n"))),
75    }
76}
77
78/// Validate AgentScript source code without returning the full AST.
79///
80/// Returns `true` if the source is valid, or throws an error with
81/// the parse errors if invalid.
82///
83/// # Arguments
84/// * `source` - The AgentScript source code to validate
85///
86/// # Returns
87/// * `Ok(true)` - The source is valid AgentScript
88/// * `Err(JsValue)` - Error messages if parsing fails
89#[wasm_bindgen]
90pub fn validate_agent(source: &str) -> Result<bool, JsValue> {
91    match crate::parse(source) {
92        Ok(ast) => {
93            let issues = crate::validate_ast(&ast);
94            let errors: Vec<String> = issues
95                .into_iter()
96                .filter(|i| i.severity == Severity::Error)
97                .map(|i| i.message)
98                .collect();
99
100            if errors.is_empty() {
101                Ok(true)
102            } else {
103                Err(JsValue::from_str(&errors.join("\n")))
104            }
105        }
106        Err(errs) => Err(JsValue::from_str(&errs.join("\n"))),
107    }
108}
109
110/// Validate AgentScript source code and return structured errors and warnings.
111///
112/// # Arguments
113/// * `source` - The AgentScript source code to validate
114///
115/// # Returns
116/// * `Ok(JsValue)` - Object with `errors` and `warnings` arrays
117/// * `Err(JsValue)` - Error message if serialization fails
118#[wasm_bindgen]
119pub fn validate_agent_semantic(source: &str) -> Result<JsValue, JsValue> {
120    let (errors, warnings) = match crate::parse(source) {
121        Ok(ast) => {
122            let issues = crate::validate_ast(&ast);
123            issues
124                .into_iter()
125                .partition(|i| i.severity == Severity::Error)
126        }
127        Err(parse_errs) => {
128            let errors = parse_errs
129                .into_iter()
130                .map(|msg| crate::validation::SemanticError {
131                    message: msg,
132                    span: None,
133                    severity: Severity::Error,
134                    hint: None,
135                })
136                .collect();
137            (errors, vec![])
138        }
139    };
140
141    let report = ValidationReport { errors, warnings };
142    serde_wasm_bindgen::to_value(&report).map_err(|e| JsValue::from_str(&e.to_string()))
143}
144
145/// Get the version of the parser.
146#[wasm_bindgen]
147pub fn version() -> String {
148    env!("CARGO_PKG_VERSION").to_string()
149}
150
151/// Serialize an AST back to AgentScript source code.
152///
153/// Takes a JavaScript object representing an AST (as returned by `parse_agent`)
154/// and converts it back to AgentScript source code.
155///
156/// # Arguments
157/// * `ast` - The AST as a JavaScript object
158///
159/// # Returns
160/// * `Ok(String)` - The serialized AgentScript source code
161/// * `Err(JsValue)` - Error message if serialization fails
162#[wasm_bindgen]
163pub fn serialize_agent(ast: JsValue) -> Result<String, JsValue> {
164    let agent: crate::ast::AgentFile = serde_wasm_bindgen::from_value(ast)
165        .map_err(|e| JsValue::from_str(&format!("Failed to deserialize AST: {}", e)))?;
166    Ok(crate::serialize(&agent))
167}
168
169/// Parse AgentScript source, then serialize it back.
170///
171/// This is useful for formatting/normalizing AgentScript code.
172///
173/// # Arguments
174/// * `source` - The AgentScript source code to parse and reserialize
175///
176/// # Returns
177/// * `Ok(String)` - The normalized AgentScript source code
178/// * `Err(JsValue)` - Error message if parsing or serialization fails
179#[wasm_bindgen]
180pub fn normalize_agent(source: &str) -> Result<String, JsValue> {
181    match crate::parse(source) {
182        Ok(ast) => Ok(crate::serialize(&ast)),
183        Err(errs) => Err(JsValue::from_str(&errs.join("\n"))),
184    }
185}