use crate::runtime::microkernel::{Pid, SyscallInterface, WasmMicroKernel};
use crate::runtime::syscalls::{SyscallArgs, SyscallResult};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectBundle {
pub name: String,
pub language: String,
pub entry_point: String,
pub files: HashMap<String, Vec<u8>>,
pub dependencies: Vec<String>,
pub metadata: ProjectMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectMetadata {
pub version: String,
pub description: Option<String>,
pub author: Option<String>,
pub license: Option<String>,
}
#[allow(dead_code)]
pub trait DevServer: Send + Sync {
fn start(&self, port: u16) -> Result<()>;
fn stop(&self) -> Result<()>;
fn reload(&self) -> Result<()>;
fn get_status(&self) -> DevServerStatus;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DevServerStatus {
Stopped,
Starting,
Running(u16),
Error(String),
}
#[allow(dead_code)]
pub trait LanguageRuntime: Send + Sync {
fn name(&self) -> &str;
fn extensions(&self) -> &[&str];
fn entry_files(&self) -> &[&str];
fn load_wasm_binary(&self) -> Result<Vec<u8>>;
fn create_syscall_interface(&self) -> Box<dyn SyscallInterface>;
fn supports_hot_reload(&self) -> bool;
fn supports_debugging(&self) -> bool;
fn create_dev_server(&self) -> Option<Box<dyn DevServer>>;
fn detect_project(&self, project_path: &str) -> bool;
fn prepare_project(&self, project_path: &str) -> Result<ProjectBundle>;
fn run_project(&self, bundle: ProjectBundle, kernel: &mut WasmMicroKernel) -> Result<Pid>;
fn handle_syscall(&self, pid: Pid, syscall_num: u32, args: SyscallArgs) -> SyscallResult;
}
pub struct LanguageRuntimeRegistry {
runtimes: HashMap<String, Box<dyn LanguageRuntime>>,
}
impl Default for LanguageRuntimeRegistry {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
impl LanguageRuntimeRegistry {
pub fn new() -> Self {
Self {
runtimes: HashMap::new(),
}
}
pub fn register_builtin_runtimes() -> Self {
let mut registry = Self::new();
{
use crate::runtime::languages::nodejs::NodeJSRuntime;
registry.register("nodejs", Box::new(NodeJSRuntime::new()));
}
registry
}
pub fn register(&mut self, name: &str, runtime: Box<dyn LanguageRuntime>) {
self.runtimes.insert(name.to_string(), runtime);
}
pub fn get_runtime(&self, name: &str) -> Option<&dyn LanguageRuntime> {
self.runtimes.get(name).map(|r| r.as_ref())
}
pub fn get_runtime_mut(&mut self, name: &str) -> Option<&mut dyn LanguageRuntime> {
if let Some(runtime) = self.runtimes.get_mut(name) {
Some(runtime.as_mut())
} else {
None
}
}
pub fn list_runtimes(&self) -> Vec<String> {
self.runtimes.keys().cloned().collect()
}
pub fn detect_project_language(&self, project_path: &str) -> Option<&str> {
for (name, runtime) in &self.runtimes {
if runtime.detect_project(project_path) {
return Some(name);
}
}
None
}
pub fn get_runtime_by_extension(&self, extension: &str) -> Option<&str> {
for (name, runtime) in &self.runtimes {
if runtime.extensions().contains(&extension) {
return Some(name);
}
}
None
}
pub fn unregister(&mut self, name: &str) -> Option<Box<dyn LanguageRuntime>> {
self.runtimes.remove(name)
}
pub fn has_runtime(&self, name: &str) -> bool {
self.runtimes.contains_key(name)
}
pub fn get_runtime_info(&self, name: &str) -> Option<RuntimeInfo> {
self.runtimes.get(name).map(|runtime| RuntimeInfo {
name: runtime.name().to_string(),
extensions: runtime.extensions().iter().map(|s| s.to_string()).collect(),
entry_files: runtime
.entry_files()
.iter()
.map(|s| s.to_string())
.collect(),
supports_hot_reload: runtime.supports_hot_reload(),
supports_debugging: runtime.supports_debugging(),
})
}
pub fn get_all_runtime_info(&self) -> Vec<RuntimeInfo> {
self.runtimes
.keys()
.filter_map(|name| self.get_runtime_info(name))
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeInfo {
pub name: String,
pub extensions: Vec<String>,
pub entry_files: Vec<String>,
pub supports_hot_reload: bool,
pub supports_debugging: bool,
}
#[allow(dead_code)]
pub struct BaseRuntime {
pub name: String,
pub extensions: Vec<String>,
pub entry_files: Vec<String>,
pub wasm_binary_path: Option<String>,
pub supports_hot_reload: bool,
pub supports_debugging: bool,
}
#[allow(dead_code)]
impl BaseRuntime {
pub fn new(name: String) -> Self {
Self {
name,
extensions: vec![],
entry_files: vec![],
wasm_binary_path: None,
supports_hot_reload: false,
supports_debugging: false,
}
}
pub fn with_extensions(mut self, extensions: Vec<String>) -> Self {
self.extensions = extensions;
self
}
pub fn with_entry_files(mut self, entry_files: Vec<String>) -> Self {
self.entry_files = entry_files;
self
}
pub fn with_wasm_binary(mut self, path: String) -> Self {
self.wasm_binary_path = Some(path);
self
}
pub fn with_hot_reload(mut self, enabled: bool) -> Self {
self.supports_hot_reload = enabled;
self
}
pub fn with_debugging(mut self, enabled: bool) -> Self {
self.supports_debugging = enabled;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockRuntime {
base: BaseRuntime,
}
impl MockRuntime {
fn new() -> Self {
Self {
base: BaseRuntime::new("mock".to_string())
.with_extensions(vec!["mock".to_string()])
.with_entry_files(vec!["mock.toml".to_string()]),
}
}
}
impl LanguageRuntime for MockRuntime {
fn name(&self) -> &str {
&self.base.name
}
fn extensions(&self) -> &[&str] {
&[]
}
fn entry_files(&self) -> &[&str] {
&[]
}
fn load_wasm_binary(&self) -> Result<Vec<u8>> {
Ok(vec![0x00, 0x61, 0x73, 0x6d]) }
fn create_syscall_interface(&self) -> Box<dyn SyscallInterface> {
unimplemented!("Mock runtime doesn't implement syscall interface")
}
fn supports_hot_reload(&self) -> bool {
self.base.supports_hot_reload
}
fn supports_debugging(&self) -> bool {
self.base.supports_debugging
}
fn create_dev_server(&self) -> Option<Box<dyn DevServer>> {
None
}
fn detect_project(&self, _project_path: &str) -> bool {
false
}
fn prepare_project(&self, _project_path: &str) -> Result<ProjectBundle> {
Ok(ProjectBundle {
name: "mock-project".to_string(),
language: "mock".to_string(),
entry_point: "main.mock".to_string(),
files: HashMap::new(),
dependencies: vec![],
metadata: ProjectMetadata {
version: "1.0.0".to_string(),
description: None,
author: None,
license: None,
},
})
}
fn run_project(
&self,
_bundle: ProjectBundle,
_kernel: &mut WasmMicroKernel,
) -> Result<Pid> {
Ok(1)
}
fn handle_syscall(
&self,
_pid: Pid,
_syscall_num: u32,
_args: SyscallArgs,
) -> SyscallResult {
SyscallResult::Error("Mock runtime doesn't handle syscalls".to_string())
}
}
#[test]
fn test_registry_creation() {
let registry = LanguageRuntimeRegistry::new();
assert_eq!(registry.list_runtimes().len(), 0);
}
#[test]
fn test_runtime_registration() {
let mut registry = LanguageRuntimeRegistry::new();
let mock_runtime = MockRuntime::new();
registry.register("mock", Box::new(mock_runtime));
assert!(registry.has_runtime("mock"));
assert_eq!(registry.list_runtimes(), vec!["mock"]);
}
#[test]
fn test_runtime_retrieval() {
let mut registry = LanguageRuntimeRegistry::new();
let mock_runtime = MockRuntime::new();
registry.register("mock", Box::new(mock_runtime));
let runtime = registry.get_runtime("mock");
assert!(runtime.is_some());
assert_eq!(runtime.unwrap().name(), "mock");
}
#[test]
fn test_runtime_unregistration() {
let mut registry = LanguageRuntimeRegistry::new();
let mock_runtime = MockRuntime::new();
registry.register("mock", Box::new(mock_runtime));
assert!(registry.has_runtime("mock"));
let removed = registry.unregister("mock");
assert!(removed.is_some());
assert!(!registry.has_runtime("mock"));
}
}