use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::logging::LogTrailSystem;
use crate::runtime::dev_server::DevServerManager;
use crate::runtime::microkernel::{Pid, WasmInstance, WasmMicroKernel};
use crate::runtime::network_namespace::NetworkNamespace;
use crate::runtime::registry::{DevServerStatus, LanguageRuntimeRegistry};
use crate::runtime::syscalls::{SyscallArgs, SyscallHandler, SyscallResult};
pub struct MultiLanguageKernel {
base_kernel: WasmMicroKernel,
language_registry: LanguageRuntimeRegistry,
active_runtimes: Arc<Mutex<HashMap<String, WasmInstance>>>,
dev_server_manager: Arc<DevServerManager>,
syscall_handler: Arc<Mutex<SyscallHandler>>,
process_languages: Arc<Mutex<HashMap<Pid, String>>>,
network_namespaces: Arc<Mutex<HashMap<Pid, Arc<NetworkNamespace>>>>,
log_system: Arc<LogTrailSystem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OsRunConfig {
pub project_path: String,
pub language: Option<String>,
pub dev_mode: bool,
pub port: Option<u16>,
pub hot_reload: bool,
pub debugging: bool,
pub expose: bool,
pub tunnel_server: Option<String>,
pub tunnel_secret: Option<String>,
pub allow_cors: bool,
}
impl Default for MultiLanguageKernel {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
impl MultiLanguageKernel {
pub fn new() -> Self {
let base_kernel = WasmMicroKernel::new();
let syscall_handler = SyscallHandler::new(base_kernel.clone());
Self {
base_kernel: base_kernel.clone(),
language_registry: LanguageRuntimeRegistry::register_builtin_runtimes(),
active_runtimes: Arc::new(Mutex::new(HashMap::new())),
dev_server_manager: Arc::new(DevServerManager::new()),
syscall_handler: Arc::new(Mutex::new(syscall_handler)),
process_languages: Arc::new(Mutex::new(HashMap::new())),
network_namespaces: Arc::new(Mutex::new(HashMap::new())),
log_system: Arc::new(LogTrailSystem::new()),
}
}
pub fn start(&self) -> Result<()> {
self.base_kernel.start_scheduler()?;
self.base_kernel.init_vfs()?;
println!("✅ WASI filesystem initialized");
Ok(())
}
pub fn mount_project(&self, project_path: &str) -> Result<()> {
let wasi_fs = self.base_kernel.wasi_filesystem();
let project_name = std::path::Path::new(project_path)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("project");
let mount_path = format!("/{project_name}");
wasi_fs.mount(&mount_path, project_path)?;
println!("✅ Project mounted at {mount_path} -> {project_path}");
Ok(())
}
pub fn wasi_filesystem(&self) -> &crate::runtime::wasi_fs::WasiFilesystem {
self.base_kernel.wasi_filesystem()
}
pub fn stop(&self) -> Result<()> {
for (pid, _, _) in self.dev_server_manager.list_servers() {
let _ = self.dev_server_manager.stop_server(pid);
}
{
let mut runtimes = self.active_runtimes.lock().unwrap();
runtimes.clear();
}
self.base_kernel.stop_scheduler()?;
Ok(())
}
pub fn auto_detect_and_run(&mut self, config: OsRunConfig) -> Result<Pid> {
let language = match config.language.clone() {
Some(lang) => lang,
None => {
match self
.language_registry
.detect_project_language(&config.project_path)
{
Some(lang) => lang.to_string(),
None => {
return Err(anyhow::anyhow!(
"Could not auto-detect language for project: {}. Please specify --language",
config.project_path
))
}
}
}
};
self.run_project_with_language(config, &language)
}
pub fn run_project_with_language(
&mut self,
config: OsRunConfig,
language: &str,
) -> Result<Pid> {
self.ensure_runtime_loaded(language)?;
let runtime = self
.language_registry
.get_runtime(language)
.ok_or_else(|| anyhow::anyhow!("Runtime not found: {language}"))?;
let bundle = runtime.prepare_project(&config.project_path)?;
let pid = runtime.run_project(bundle, &mut self.base_kernel)?;
{
let mut process_languages = self.process_languages.lock().unwrap();
process_languages.insert(pid, language.to_string());
}
{
let network_ns = Arc::new(NetworkNamespace::new(pid));
let mut namespaces = self.network_namespaces.lock().unwrap();
namespaces.insert(pid, network_ns);
}
if config.dev_mode {
self.setup_dev_environment(pid, language, &config)?;
}
Ok(pid)
}
fn ensure_runtime_loaded(&mut self, runtime_name: &str) -> Result<()> {
{
let runtimes = self.active_runtimes.lock().unwrap();
if runtimes.contains_key(runtime_name) {
return Ok(()); }
}
let runtime = self
.language_registry
.get_runtime(runtime_name)
.ok_or_else(|| anyhow::anyhow!("Runtime not found: {runtime_name}"))?;
let wasm_binary = runtime.load_wasm_binary()?;
let instance = WasmInstance {
binary: wasm_binary,
exports: HashMap::new(),
memory_regions: vec![],
};
{
let mut runtimes = self.active_runtimes.lock().unwrap();
runtimes.insert(runtime_name.to_string(), instance);
}
Ok(())
}
fn setup_dev_environment(&self, pid: Pid, language: &str, config: &OsRunConfig) -> Result<()> {
let runtime = self
.language_registry
.get_runtime(language)
.ok_or_else(|| anyhow::anyhow!("Runtime not found: {language}"))?;
if runtime.create_dev_server().is_some() {
let port = config.port.unwrap_or_else(|| 8000 + (pid as u16));
let project_root = format!("/projects/{pid}");
let wasi_fs = self.base_kernel.wasi_filesystem_arc();
self.dev_server_manager
.start_server(pid, port, project_root, wasi_fs)?;
println!("✅ Dev server started for PID {pid} on port {port}");
}
if config.hot_reload && runtime.supports_hot_reload() {
self.setup_hot_reload(pid, language)?;
}
if config.debugging && runtime.supports_debugging() {
self.setup_debugging(pid, language)?;
}
Ok(())
}
fn setup_hot_reload(&self, _pid: Pid, _language: &str) -> Result<()> {
Ok(())
}
fn setup_debugging(&self, _pid: Pid, _language: &str) -> Result<()> {
Ok(())
}
pub fn handle_syscall(
&mut self,
pid: Pid,
syscall_num: u32,
args: SyscallArgs,
) -> SyscallResult {
if let Some(language) = self.get_process_language(pid) {
if let Some(runtime) = self.language_registry.get_runtime(&language) {
match runtime.handle_syscall(pid, syscall_num, args.clone()) {
SyscallResult::Success(result) => return SyscallResult::Success(result),
SyscallResult::Error(_) => {
}
}
}
}
let mut handler = self.syscall_handler.lock().unwrap();
handler.handle_syscall(pid, syscall_num, args)
}
pub fn get_process_language(&self, pid: Pid) -> Option<String> {
let process_languages = self.process_languages.lock().unwrap();
process_languages.get(&pid).cloned()
}
pub fn list_processes_with_languages(&self) -> Vec<(Pid, String, String)> {
let processes = self.base_kernel.list_processes();
let process_languages = self.process_languages.lock().unwrap();
processes
.into_iter()
.map(|process| {
let language = process_languages
.get(&process.pid)
.cloned()
.unwrap_or_else(|| "unknown".to_string());
(process.pid, process.name, language)
})
.collect()
}
pub fn get_dev_server_status(&self, pid: Pid) -> Option<DevServerStatus> {
self.dev_server_manager.get_status(pid)
}
pub fn kill_process(&mut self, pid: Pid) -> Result<()> {
let _ = self.dev_server_manager.stop_server(pid);
{
let mut process_languages = self.process_languages.lock().unwrap();
process_languages.remove(&pid);
}
{
let mut namespaces = self.network_namespaces.lock().unwrap();
namespaces.remove(&pid);
}
self.base_kernel.kill_process(pid)?;
Ok(())
}
pub fn get_statistics(&self) -> KernelStatistics {
let memory_stats = self.base_kernel.get_memory_stats();
let active_runtimes = {
let runtimes = self.active_runtimes.lock().unwrap();
runtimes.keys().cloned().collect()
};
let active_dev_servers = self.dev_server_manager.list_servers().len();
let os = std::env::consts::OS.to_string();
let arch = std::env::consts::ARCH.to_string();
let kernel_version = env!("CARGO_PKG_VERSION").to_string();
let wasi_capabilities = vec![
"wasi_snapshot_preview1".to_string(),
"filesystem".to_string(),
"networking".to_string(),
"process".to_string(),
];
let wasi_fs = self.base_kernel.wasi_filesystem();
let fs_stats = wasi_fs.get_stats();
let filesystem_mounts = fs_stats.total_mounts;
let supported_languages = self.language_registry.list_runtimes();
KernelStatistics {
total_memory_usage: memory_stats.get("total_memory").copied().unwrap_or(0),
active_processes: memory_stats.get("process_count").copied().unwrap_or(0),
active_runtimes,
active_dev_servers,
os,
arch,
kernel_version,
wasi_capabilities,
filesystem_mounts,
supported_languages,
}
}
pub fn base_kernel(&self) -> &WasmMicroKernel {
&self.base_kernel
}
pub fn registry(&self) -> &LanguageRuntimeRegistry {
&self.language_registry
}
pub fn registry_mut(&mut self) -> &mut LanguageRuntimeRegistry {
&mut self.language_registry
}
pub fn log_system(&self) -> Arc<LogTrailSystem> {
Arc::clone(&self.log_system)
}
pub fn get_network_namespace(&self, pid: Pid) -> Option<Arc<NetworkNamespace>> {
let namespaces = self.network_namespaces.lock().unwrap();
namespaces.get(&pid).cloned()
}
pub fn get_network_stats(
&self,
) -> HashMap<Pid, crate::runtime::network_namespace::NetworkStats> {
let namespaces = self.network_namespaces.lock().unwrap();
namespaces
.iter()
.map(|(pid, ns)| (*pid, ns.get_stats()))
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelStatistics {
pub total_memory_usage: usize,
pub active_processes: usize,
pub active_runtimes: Vec<String>,
pub active_dev_servers: usize,
pub os: String,
pub arch: String,
pub kernel_version: String,
pub wasi_capabilities: Vec<String>,
pub filesystem_mounts: usize,
pub supported_languages: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kernel_creation() {
let kernel = MultiLanguageKernel::new();
let stats = kernel.get_statistics();
assert_eq!(stats.active_processes, 0);
assert_eq!(stats.active_runtimes.len(), 0);
}
#[test]
fn test_kernel_start_stop() {
let kernel = MultiLanguageKernel::new();
assert!(kernel.start().is_ok());
assert!(kernel.stop().is_ok());
}
}