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 "i64".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("i64");
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, i64");
let mut parts = inner.splitn(2, ',');
let k = parts.next().unwrap_or("String").trim();
let v = parts.next().unwrap_or("i64").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 default_return_expr(ret: &str, method_name: &str) -> String {
let r = ret.trim();
if r == "()" {
return "()".to_string();
}
if r == "bool" {
return "false".to_string();
}
if r == "i64" || r == "i32" || r == "u64" || r == "u32" || r == "usize" {
return "0".to_string();
}
if r == "f64" || r == "f32" {
return "0.0".to_string();
}
if r == "String" {
return "String::new()".to_string();
}
if r.starts_with("Vec<") {
return "Default::default()".to_string();
}
if r.contains("HashMap") {
return "Default::default()".to_string();
}
if r.starts_with("Option<") {
return "None".to_string();
}
if r.starts_with("Result<") {
return format!(
"unimplemented!(\"native stub: {} not implemented; implement in Rust or use DAL interpreter\")",
method_name
);
}
"Default::default()".to_string()
}
fn services_to_rust(services: &[&ServiceStatement]) -> String {
let mut out = String::new();
out.push_str("// Generated by DAL native backend. Compiles for host.\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| {
let ty = p.param_type.as_deref().unwrap_or("i64");
format!("{}: {}", p.name, dal_type_to_rust(ty))
})
.collect();
let ret = method
.return_type
.as_deref()
.map(|r| dal_type_to_rust(r))
.unwrap_or_else(|| "()".to_string());
let default_ret = default_return_expr(&ret, &method.name);
out.push_str(&format!(
" /// Stub: replace with DAL-implemented logic or keep default return.\n pub fn {}({}) -> {} {{\n {}\n }}\n",
method.name,
params_str.join(", "),
ret,
default_ret
));
}
out.push_str("}\n\n");
}
out
}
fn check_rust_available() -> bool {
if let Some(available) = super::get_compiler_available_override() {
return available;
}
Command::new("cargo")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
pub struct NativeBackend;
impl CompileBackend for NativeBackend {
fn compile(
&self,
_program: &Program,
services: &[&ServiceStatement],
opts: &CompileOptions,
) -> Result<CompileArtifacts, CompileError> {
if !check_rust_available() {
return Err(CompileError::CompilerNotFound {
target: "native".to_string(),
hint: "Install Rust: https://rustup.rs".to_string(),
});
}
std::fs::create_dir_all(&opts.output_dir).map_err(CompileError::Io)?;
let build_dir = opts.output_dir.join("native_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_native"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["rlib"]
"#;
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", "--release"])
.output()
.map_err(CompileError::Io)?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(CompileError::Backend(format!(
"cargo native build failed: {}",
stderr.trim()
)));
}
let mut artifact_paths = Vec::new();
let rlib_name = "libdal_native.rlib";
let built_rlib = build_dir.join("target").join("release").join(rlib_name);
if built_rlib.exists() {
let out_rlib = opts.output_dir.join(rlib_name);
std::fs::copy(&built_rlib, &out_rlib).map_err(CompileError::Io)?;
artifact_paths.push(out_rlib);
}
let service_names: Vec<String> = services.iter().map(|s| s.name.clone()).collect();
Ok(CompileArtifacts {
target: "native".to_string(),
service_names,
artifact_paths,
stub: false,
})
}
}