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}