#![cfg(target_arch = "wasm32")]
use crate::backend::transpiler::Transpiler;
use crate::frontend::parser::Parser;
use js_sys::Promise;
#[cfg(test)]
use proptest::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
#[wasm_bindgen]
pub struct RuchyWasm {
transpiler: Transpiler,
}
#[wasm_bindgen]
impl RuchyWasm {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
console_error_panic_hook::set_once();
Self {
transpiler: Transpiler::new(),
}
}
#[wasm_bindgen]
pub fn compile(&self, source: &str) -> Result<String, JsValue> {
let mut parser = Parser::new(source);
let ast = parser
.parse()
.map_err(|e| JsValue::from(js_sys::Error::new(&format!("Parse error: {}", e))))?;
let rust_code = self
.transpiler
.transpile(&ast)
.map_err(|e| JsValue::from(js_sys::Error::new(&format!("Transpile error: {}", e))))?;
Ok(rust_code.to_string())
}
#[wasm_bindgen]
pub fn validate(&self, source: &str) -> bool {
Parser::new(source).parse().is_ok()
}
#[wasm_bindgen(getter)]
pub fn version(&self) -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn compile_async(&self, source: String) -> Promise {
let transpiler = self.transpiler.clone();
future_to_promise(async move {
let mut parser = Parser::new(&source);
let ast = parser
.parse()
.map_err(|e| JsValue::from(js_sys::Error::new(&format!("Parse error: {}", e))))?;
let rust_code = transpiler.transpile(&ast).map_err(|e| {
JsValue::from(js_sys::Error::new(&format!("Transpile error: {}", e)))
})?;
Ok(JsValue::from_str(&rust_code.to_string()))
})
}
#[wasm_bindgen]
pub fn compile_cells_parallel(&self, sources: &js_sys::Array) -> Promise {
let transpiler = self.transpiler.clone();
let sources: Vec<String> = sources
.iter()
.map(|val| val.as_string().unwrap_or_default())
.collect();
future_to_promise(async move {
let mut results = Vec::new();
for source in sources {
let mut parser = Parser::new(&source);
match parser.parse() {
Ok(ast) => match transpiler.transpile(&ast) {
Ok(rust_code) => {
results.push(serde_json::json!({
"success": true,
"result": rust_code.to_string(),
"error": null
}));
}
Err(e) => {
results.push(serde_json::json!({
"success": false,
"result": null,
"error": format!("Transpile error: {}", e)
}));
}
},
Err(e) => {
results.push(serde_json::json!({
"success": false,
"result": null,
"error": format!("Parse error: {}", e)
}));
}
}
}
let results_array = js_sys::Array::new();
for result in results {
results_array.push(&JsValue::from_str(&result.to_string()));
}
Ok(results_array.into())
})
}
#[wasm_bindgen]
pub fn get_webworker_info(&self) -> JsValue {
let info = serde_json::json!({
"webworker_support": true,
"async_compilation": true,
"parallel_cells": true,
"max_concurrent_cells": 4,
"performance_target_ms": 10,
"features": {
"incremental_compilation": true,
"error_recovery": true,
"source_maps": false,
"fast_execution": true
}
});
JsValue::from_str(&info.to_string())
}
#[wasm_bindgen]
pub fn execute_cell_fast(&self, source: &str) -> JsValue {
let start_time = js_sys::Date::now();
let result = match self.compile(source) {
Ok(rust_code) => {
serde_json::json!({
"success": true,
"result": rust_code,
"error": null
})
}
Err(e) => {
serde_json::json!({
"success": false,
"result": null,
"error": e.as_string().unwrap_or_default()
})
}
};
let end_time = js_sys::Date::now();
let execution_time = end_time - start_time;
let performance_result = serde_json::json!({
"cell_result": result,
"performance": {
"execution_time_ms": execution_time,
"target_met": execution_time < 10.0,
"target_ms": 10,
"optimization_level": "fast"
}
});
JsValue::from_str(&performance_result.to_string())
}
#[wasm_bindgen]
pub fn benchmark_cell_execution(&self, iterations: usize) -> JsValue {
let test_cases = vec![
"let x = 42", "let y = x * 2 + 1", "fun double(n: Int) -> Int { n * 2 }", "let result = double(21)", "if x > 0 { x } else { 0 }", ];
let mut benchmark_results = Vec::new();
for (i, test_case) in test_cases.iter().enumerate() {
let mut total_time = 0.0;
let mut success_count = 0;
for _ in 0..iterations {
let start_time = js_sys::Date::now();
let result = self.compile(test_case);
let end_time = js_sys::Date::now();
total_time += end_time - start_time;
if result.is_ok() {
success_count += 1;
}
}
let avg_time = total_time / iterations as f64;
let success_rate = (success_count as f64) / (iterations as f64) * 100.0;
benchmark_results.push(serde_json::json!({
"test_case": test_case,
"test_id": i,
"avg_execution_time_ms": avg_time,
"success_rate_percent": success_rate,
"target_met": avg_time < 10.0,
"iterations": iterations
}));
}
let overall_avg = benchmark_results
.iter()
.map(|r| r["avg_execution_time_ms"].as_f64().unwrap_or(0.0))
.sum::<f64>()
/ benchmark_results.len() as f64;
let result = serde_json::json!({
"benchmark_results": benchmark_results,
"summary": {
"overall_avg_ms": overall_avg,
"target_ms": 10,
"target_met": overall_avg < 10.0,
"total_iterations": iterations * test_cases.len()
}
});
JsValue::from_str(&result.to_string())
}
}
#[wasm_bindgen]
pub struct WebWorkerRuntime {
max_workers: usize,
active_workers: usize,
}
#[wasm_bindgen]
impl WebWorkerRuntime {
#[wasm_bindgen(constructor)]
pub fn new(max_workers: usize) -> Self {
Self {
max_workers: max_workers.max(1).min(8), active_workers: 0,
}
}
#[wasm_bindgen]
pub fn execute_with_workers(&mut self, task_data: &str) -> Promise {
let max_workers = self.max_workers;
let task_data = task_data.to_string();
future_to_promise(async move {
let start_time = js_sys::Date::now();
let compiler = RuchyWasm::new();
let result = compiler.compile(&task_data);
let end_time = js_sys::Date::now();
let duration = end_time - start_time;
match result {
Ok(output) => {
let response = serde_json::json!({
"success": true,
"result": output,
"execution_time_ms": duration,
"worker_id": max_workers,
"memory_used": "optimized"
});
Ok(JsValue::from_str(&response.to_string()))
}
Err(e) => {
let response = serde_json::json!({
"success": false,
"error": e.as_string().unwrap_or_default(),
"execution_time_ms": duration,
"worker_id": max_workers
});
Ok(JsValue::from_str(&response.to_string()))
}
}
})
}
#[wasm_bindgen]
pub fn get_worker_status(&self) -> JsValue {
let status = serde_json::json!({
"max_workers": self.max_workers,
"active_workers": self.active_workers,
"available_workers": self.max_workers - self.active_workers,
"load_factor": if self.max_workers > 0 {
(self.active_workers as f64) / (self.max_workers as f64)
} else { 0.0 }
});
JsValue::from_str(&status.to_string())
}
}
#[wasm_bindgen(start)]
pub fn wasm_init() {
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
impl RuchyWasm {
#[wasm_bindgen]
pub fn roundtrip(&self, input: JsValue) -> JsValue {
input
}
#[wasm_bindgen]
pub fn trigger_panic(&self, message: &str) {
panic!("{}", message);
}
#[wasm_bindgen]
pub fn call_js_callback(&self, callback: &js_sys::Function) -> JsValue {
match callback.call0(&JsValue::NULL) {
Ok(result) => {
let result_str = result
.as_string()
.unwrap_or_else(|| format!("{:?}", result));
serde_wasm_bindgen::to_value(&serde_json::json!({
"is_err": false,
"value": result_str
}))
.unwrap_or(JsValue::NULL)
}
Err(e) => serde_wasm_bindgen::to_value(&serde_json::json!({
"is_err": true,
"error": format!("{:?}", e)
}))
.unwrap_or(JsValue::NULL),
}
}
#[wasm_bindgen]
pub fn process_bytes(&self, data: &[u8]) -> ProcessResult {
ProcessResult::new(data.to_vec())
}
#[wasm_bindgen]
pub fn async_operation(&self, value: i32) -> Promise {
future_to_promise(async move {
Ok(JsValue::from(value))
})
}
}
#[wasm_bindgen]
pub struct ProcessResult {
data: Vec<u8>,
}
#[wasm_bindgen]
impl ProcessResult {
fn new(data: Vec<u8>) -> Self {
Self { data }
}
#[wasm_bindgen(getter)]
pub fn data(&self) -> js_sys::Uint8Array {
js_sys::Uint8Array::from(&self.data[..])
}
#[wasm_bindgen]
pub fn free(self) {
drop(self);
}
}
#[cfg(test)]
mod property_tests_wasm_bindings {
use super::*;
use proptest::prelude::*;
use proptest::proptest;
proptest! {
#[test]
fn test_new_never_panics(input: String) {
let _input = if input.len() > 100 { &input[..100] } else { &input[..] };
let _ = std::panic::catch_unwind(|| {
let _ruchy = RuchyWasm::new();
});
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rust_repl_new() {
let repl = RuchyRepl::new();
assert!(repl.engine.is_none());
}
#[test]
fn test_rust_repl_eval_simple() {
let mut repl = RuchyRepl::new();
let result = repl.eval("2 + 2");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_let() {
let mut repl = RuchyRepl::new();
let result = repl.eval("let x = 10");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_function() {
let mut repl = RuchyRepl::new();
let result = repl.eval("fun add(a, b) { a + b }");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_invalid() {
let mut repl = RuchyRepl::new();
let result = repl.eval("invalid++syntax");
assert!(result.is_some()); }
#[test]
fn test_rust_repl_eval_empty() {
let mut repl = RuchyRepl::new();
let result = repl.eval("");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_whitespace() {
let mut repl = RuchyRepl::new();
let result = repl.eval(" ");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_comment() {
let mut repl = RuchyRepl::new();
let result = repl.eval("// just a comment");
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_multiline() {
let mut repl = RuchyRepl::new();
let result = repl.eval(
"let x = 1
let y = 2
x + y",
);
assert!(result.is_some());
}
#[test]
fn test_rust_repl_eval_string() {
let mut repl = RuchyRepl::new();
let result = repl.eval("\"hello world\"");
assert!(result.is_some());
}
}