use crate::cloudhv; use crate::cloudhv::{VmRecord, VmRuntime, VmSpec}; use rhai::{Array, Dynamic, Engine, EvalAltResult, Map}; // Error adapter fn hv_to_rhai(r: Result) -> Result> { r.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("cloudhv error: {}", e).into(), rhai::Position::NONE, )) }) } // Map conversions fn map_to_vmspec(spec: Map) -> Result> { let id = must_get_string(&spec, "id")?; let kernel_path = get_string(&spec, "kernel_path"); let initramfs_path = get_string(&spec, "initramfs_path"); let firmware_path = get_string(&spec, "firmware_path"); let disk_path = must_get_string(&spec, "disk_path")?; let api_socket = get_string(&spec, "api_socket").unwrap_or_else(|| "".to_string()); let vcpus = get_int(&spec, "vcpus").unwrap_or(1) as u32; let memory_mb = get_int(&spec, "memory_mb").unwrap_or(512) as u32; let cmdline = get_string(&spec, "cmdline"); let extra_args = get_string_array(&spec, "extra_args"); Ok(VmSpec { id, kernel_path, initramfs_path, firmware_path, disk_path, api_socket, vcpus, memory_mb, cmdline, extra_args, net_profile: None, }) } fn vmspec_to_map(s: &VmSpec) -> Map { let mut m = Map::new(); m.insert("id".into(), s.id.clone().into()); if let Some(k) = &s.kernel_path { m.insert("kernel_path".into(), k.clone().into()); } else { m.insert("kernel_path".into(), Dynamic::UNIT); } if let Some(ir) = &s.initramfs_path { m.insert("initramfs_path".into(), ir.clone().into()); } else { m.insert("initramfs_path".into(), Dynamic::UNIT); } if let Some(fw) = &s.firmware_path { m.insert("firmware_path".into(), fw.clone().into()); } else { m.insert("firmware_path".into(), Dynamic::UNIT); } m.insert("disk_path".into(), s.disk_path.clone().into()); m.insert("api_socket".into(), s.api_socket.clone().into()); m.insert("vcpus".into(), (s.vcpus as i64).into()); m.insert("memory_mb".into(), (s.memory_mb as i64).into()); if let Some(c) = &s.cmdline { m.insert("cmdline".into(), c.clone().into()); } else { m.insert("cmdline".into(), Dynamic::UNIT); } if let Some(arr) = &s.extra_args { let mut a = Array::new(); for s in arr { a.push(s.clone().into()); } m.insert("extra_args".into(), a.into()); } else { m.insert("extra_args".into(), Dynamic::UNIT); } // net_profile not exposed in Rhai yet; return UNIT for now m.insert("net_profile".into(), Dynamic::UNIT); m } fn vmruntime_to_map(r: &VmRuntime) -> Map { let mut m = Map::new(); match r.pid { Some(p) => m.insert("pid".into(), (p as i64).into()), None => m.insert("pid".into(), Dynamic::UNIT), }; m.insert("status".into(), r.status.clone().into()); m.insert("log_file".into(), r.log_file.clone().into()); m } fn vmrecord_to_map(rec: &VmRecord) -> Map { let mut m = Map::new(); m.insert("spec".into(), vmspec_to_map(&rec.spec).into()); m.insert("runtime".into(), vmruntime_to_map(&rec.runtime).into()); m } // Helpers for reading Rhai Map fields fn must_get_string(m: &Map, k: &str) -> Result> { match m.get(k) { Some(v) if v.is_string() => Ok(v.clone().cast::()), _ => Err(Box::new(EvalAltResult::ErrorRuntime( format!("missing or non-string field '{}'", k).into(), rhai::Position::NONE, ))), } } fn get_string(m: &Map, k: &str) -> Option { m.get(k).and_then(|v| if v.is_string() { Some(v.clone().cast::()) } else { None }) } fn get_int(m: &Map, k: &str) -> Option { m.get(k).and_then(|v| v.as_int().ok()) } fn get_string_array(m: &Map, k: &str) -> Option> { m.get(k).and_then(|v| { if v.is_array() { let arr = v.clone().cast::(); let mut out = vec![]; for it in arr { if it.is_string() { out.push(it.cast::()); } } Some(out) } else { None } }) } // Rhai-exposed functions pub fn cloudhv_vm_create(spec: Map) -> Result> { let s = map_to_vmspec(spec)?; hv_to_rhai(cloudhv::vm_create(&s)) } pub fn cloudhv_vm_start(id: &str) -> Result<(), Box> { hv_to_rhai(cloudhv::vm_start(id)) } pub fn cloudhv_vm_stop(id: &str, force: bool) -> Result<(), Box> { hv_to_rhai(cloudhv::vm_stop(id, force)) } pub fn cloudhv_vm_delete(id: &str, delete_disks: bool) -> Result<(), Box> { hv_to_rhai(cloudhv::vm_delete(id, delete_disks)) } pub fn cloudhv_vm_list() -> Result> { let vms = hv_to_rhai(cloudhv::vm_list())?; let mut arr = Array::new(); for rec in vms { arr.push(vmrecord_to_map(&rec).into()); } Ok(arr) } pub fn cloudhv_vm_info(id: &str) -> Result> { let rec = hv_to_rhai(cloudhv::vm_info(id))?; Ok(vmrecord_to_map(&rec)) } // Module registration pub fn register_cloudhv_module(engine: &mut Engine) -> Result<(), Box> { engine.register_fn("cloudhv_vm_create", cloudhv_vm_create); engine.register_fn("cloudhv_vm_start", cloudhv_vm_start); engine.register_fn("cloudhv_vm_stop", cloudhv_vm_stop); engine.register_fn("cloudhv_vm_delete", cloudhv_vm_delete); engine.register_fn("cloudhv_vm_list", cloudhv_vm_list); engine.register_fn("cloudhv_vm_info", cloudhv_vm_info); Ok(()) }