use crate::compile::{CompileArtifacts, CompileBackend, CompileError, CompileOptions};
use crate::parser::ast::{Program, ServiceStatement};
use std::process::Command;
fn dal_type_to_rust(dal_type: &str) -> String {
let t = dal_type.trim();
if t.is_empty() || t == "int" {
return "i32".to_string();
}
if t == "string" {
return "String".to_string();
}
if t == "bool" {
return "bool".to_string();
}
if t.starts_with("list<") || t.starts_with("vector<") {
let inner = t
.strip_prefix("list<")
.or_else(|| t.strip_prefix("vector<"))
.and_then(|s| s.strip_suffix('>'))
.unwrap_or("i32");
return format!("Vec<{}>", dal_type_to_rust(inner));
}
if t.starts_with("map<") {
let inner = t
.strip_prefix("map<")
.and_then(|s| s.strip_suffix('>'))
.unwrap_or("string, i32");
let mut parts = inner.splitn(2, ',');
let k = parts.next().unwrap_or("String").trim();
let v = parts.next().unwrap_or("i32").trim();
return format!(
"std::collections::HashMap<{}, {}>",
dal_type_to_rust(k),
dal_type_to_rust(v)
);
}
if t == "any" || t == "float" {
return "f64".to_string();
}
t.to_string()
}
fn services_to_rust(services: &[&ServiceStatement]) -> String {
let mut out = String::new();
out.push_str("// Generated by DAL wasm backend. Compiles to wasm32-unknown-unknown.\n");
out.push_str("#![allow(dead_code)]\n\n");
for service in services {
let mod_name = service.name.to_lowercase().replace('-', "_");
out.push_str(&format!("pub mod {} {{\n", mod_name));
if service.fields.is_empty() {
out.push_str(" pub struct State;\n");
} else {
out.push_str(" pub struct State {\n");
for field in &service.fields {
let rust_type = dal_type_to_rust(&field.field_type);
out.push_str(&format!(" pub {}: {},\n", field.name, rust_type));
}
out.push_str(" }\n");
}
for method in &service.methods {
let params_str: Vec<String> = method
.parameters
.iter()
.map(|p| format!("{}: i32", p.name))
.collect();
let fn_name = format!("{}_{}", &mod_name, method.name);
out.push_str(&format!(
" #[no_mangle]\n pub extern \"C\" fn {}({}) -> i32 {{\n 0i32\n }}\n",
fn_name,
params_str.join(", ")
));
}
out.push_str("}\n\n");
}
out
}
fn check_wasm_target_available() -> bool {
if let Some(available) = super::get_compiler_available_override() {
return available;
}
let out = Command::new("rustc")
.args(["--print", "target-list"])
.output();
match out {
Ok(o) if o.status.success() => {
let list = String::from_utf8_lossy(&o.stdout);
list.contains("wasm32-unknown-unknown")
}
_ => false,
}
}
pub struct WasmBackend;
impl CompileBackend for WasmBackend {
fn compile(
&self,
_program: &Program,
services: &[&ServiceStatement],
opts: &CompileOptions,
) -> Result<CompileArtifacts, CompileError> {
if !check_wasm_target_available() {
return Err(CompileError::CompilerNotFound {
target: "wasm".to_string(),
hint: "Install wasm32 target: rustup target add wasm32-unknown-unknown".to_string(),
});
}
std::fs::create_dir_all(&opts.output_dir).map_err(CompileError::Io)?;
let build_dir = opts.output_dir.join("wasm_build");
let src_dir = build_dir.join("src");
std::fs::create_dir_all(&src_dir).map_err(CompileError::Io)?;
let rust_code = services_to_rust(services);
let lib_path = src_dir.join("lib.rs");
std::fs::write(&lib_path, rust_code).map_err(CompileError::Io)?;
let cargo_toml = r#"
[package]
name = "dal_wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
"#;
std::fs::write(build_dir.join("Cargo.toml"), cargo_toml).map_err(CompileError::Io)?;
let output = Command::new("cargo")
.current_dir(&build_dir)
.args(["build", "--target", "wasm32-unknown-unknown", "--release"])
.output()
.map_err(CompileError::Io)?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(CompileError::Backend(format!(
"cargo wasm build failed: {}",
stderr.trim()
)));
}
let wasm_name = "dal_wasm.wasm";
let built_wasm = build_dir
.join("target")
.join("wasm32-unknown-unknown")
.join("release")
.join(wasm_name);
let mut artifact_paths = Vec::new();
if built_wasm.exists() {
let out_wasm = opts.output_dir.join(wasm_name);
std::fs::copy(&built_wasm, &out_wasm).map_err(CompileError::Io)?;
artifact_paths.push(out_wasm);
}
let service_names: Vec<String> = services.iter().map(|s| s.name.clone()).collect();
Ok(CompileArtifacts {
target: "wasm".to_string(),
service_names,
artifact_paths,
stub: false,
})
}
}