use js_sys::{Array, Function, Reflect};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{console, window};
#[derive(Debug, Clone)]
pub struct CommandOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
}
fn is_tauri() -> bool {
window()
.and_then(|w| Reflect::get(&w, &JsValue::from_str("__TAURI__")).ok())
.map(|v| !v.is_undefined())
.unwrap_or(false)
}
pub fn execute(command: String, args: Vec<String>) -> Result<CommandOutput, String> {
if is_tauri() {
execute_tauri(command, args)
} else {
execute_mock(command, args)
}
}
fn execute_tauri(command: String, args: Vec<String>) -> Result<CommandOutput, String> {
let window = window().ok_or("No window available")?;
let tauri_bridge = Reflect::get(&window, &JsValue::from_str("__WINDJAMMER_TAURI__"))
.map_err(|_| "Tauri bridge not found")?;
let exec_fn = Reflect::get(&tauri_bridge, &JsValue::from_str("executeCommandSync"))
.map_err(|_| "executeCommandSync function not found")?;
let exec_fn = exec_fn
.dyn_into::<Function>()
.map_err(|_| "executeCommandSync is not a function")?;
let js_args = Array::new();
for arg in args {
js_args.push(&JsValue::from_str(&arg));
}
exec_fn
.call2(&JsValue::NULL, &JsValue::from_str(&command), &js_args)
.map_err(|e| format!("Failed to call executeCommandSync: {:?}", e))?;
Ok(CommandOutput {
stdout: format!("✓ Executing: {} {}", command, args.join(" ")),
stderr: String::new(),
exit_code: 0,
})
}
fn execute_mock(command: String, args: Vec<String>) -> Result<CommandOutput, String> {
let full_command = format!("{} {}", command, args.join(" "));
console::log_1(&JsValue::from_str(&format!(
"🎮 Mock Execute: {}",
full_command
)));
if command.contains("wj") {
if args.iter().any(|a| a.contains("new")) {
console::log_1(&JsValue::from_str("✅ Created new Windjammer project"));
Ok(CommandOutput {
stdout: "Project created successfully!\nCreated: src/main.wj\nCreated: wj.toml"
.to_string(),
stderr: String::new(),
exit_code: 0,
})
} else if args.iter().any(|a| a.contains("build")) {
console::log_1(&JsValue::from_str("🔨 Building project..."));
Ok(CommandOutput {
stdout: "Compiling...\nBuild successful!".to_string(),
stderr: String::new(),
exit_code: 0,
})
} else if args.iter().any(|a| a.contains("run")) {
console::log_1(&JsValue::from_str("🚀 Running project..."));
Ok(CommandOutput {
stdout: "Running game...\nGame started successfully!".to_string(),
stderr: String::new(),
exit_code: 0,
})
} else {
Ok(CommandOutput {
stdout: format!("Executed: {}", full_command),
stderr: String::new(),
exit_code: 0,
})
}
} else {
console::log_1(&JsValue::from_str(&format!(
"⚠️ Command not fully supported in browser: {}",
command
)));
Ok(CommandOutput {
stdout: format!("Mock execution of: {}", full_command),
stderr: "Note: Running in browser with limited process support".to_string(),
exit_code: 0,
})
}
}
pub fn spawn(command: String, args: Vec<String>) -> Result<u32, String> {
console::log_1(&JsValue::from_str(&format!(
"⚠️ spawn() not supported in browser: {} {}",
command,
args.join(" ")
)));
Err(
"Process spawning not supported in browser. Use Web Workers for background tasks."
.to_string(),
)
}
pub fn kill(pid: u32) -> Result<(), String> {
console::log_1(&JsValue::from_str(&format!(
"⚠️ kill() not supported in browser: PID {}",
pid
)));
Err("Process killing not supported in browser".to_string())
}
pub fn current_dir() -> Result<String, String> {
Ok("/workspace".to_string())
}
pub fn set_current_dir(path: String) -> Result<(), String> {
console::log_1(&JsValue::from_str(&format!(
"📁 Current directory set to: {}",
path
)));
Ok(())
}
pub fn env(key: String) -> Option<String> {
match key.as_str() {
"HOME" => Some("/home/user".to_string()),
"USER" => Some("browser-user".to_string()),
"PATH" => Some("/usr/local/bin:/usr/bin:/bin".to_string()),
_ => None,
}
}
pub fn exit(code: i32) -> ! {
console::log_1(&JsValue::from_str(&format!(
"🛑 Exit called with code: {}",
code
)));
if let Some(window) = web_sys::window() {
let _ = window.close();
}
loop {
std::hint::spin_loop();
}
}