//! Rhai wrappers for Zinit client module functions //! //! This module provides Rhai wrappers for the functions in the Zinit client module. use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; use crate::zinit_client as client; use tokio::runtime::Runtime; use serde_json::{json, Value}; use crate::rhai::error::ToRhaiError; /// Register Zinit module functions with the Rhai engine /// /// # Arguments /// /// * `engine` - The Rhai engine to register the functions with /// /// # Returns /// /// * `Result<(), Box>` - Ok if registration was successful, Err otherwise pub fn register_zinit_module(engine: &mut Engine) -> Result<(), Box> { // Register Zinit client functions engine.register_fn("zinit_list", zinit_list); engine.register_fn("zinit_status", zinit_status); engine.register_fn("zinit_start", zinit_start); engine.register_fn("zinit_stop", zinit_stop); engine.register_fn("zinit_restart", zinit_restart); engine.register_fn("zinit_monitor", zinit_monitor); engine.register_fn("zinit_forget", zinit_forget); engine.register_fn("zinit_kill", zinit_kill); engine.register_fn("zinit_create_service", zinit_create_service); engine.register_fn("zinit_delete_service", zinit_delete_service); engine.register_fn("zinit_get_service", zinit_get_service); engine.register_fn("zinit_logs", zinit_logs); engine.register_fn("zinit_logs_all", zinit_logs_all); Ok(()) } impl ToRhaiError for Result { fn to_rhai_error(self) -> Result> { self.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Zinit error: {}", e).into(), rhai::Position::NONE )) }) } } // Helper function to get a runtime fn get_runtime() -> Result> { tokio::runtime::Runtime::new().map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Failed to create Tokio runtime: {}", e).into(), rhai::Position::NONE )) }) } // // Zinit Client Function Wrappers // /// Wrapper for zinit_client::list /// /// Lists all services managed by Zinit. pub fn zinit_list(socket_path: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { client::list(socket_path).await }); let services = result.to_rhai_error()?; // Convert HashMap to Rhai Map let mut map = Map::new(); for (name, state) in services { map.insert(name.into(), Dynamic::from(state)); } Ok(map) } /// Wrapper for zinit_client::status /// /// Gets the status of a specific service. pub fn zinit_status(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { client::status(socket_path, name).await }); let status = result.to_rhai_error()?; // Convert Status to Rhai Map let mut map = Map::new(); map.insert("name".into(), Dynamic::from(status.name)); map.insert("pid".into(), Dynamic::from(status.pid)); map.insert("state".into(), Dynamic::from(status.state)); map.insert("target".into(), Dynamic::from(status.target)); // Convert dependencies let mut deps_map = Map::new(); for (dep, state) in status.after { deps_map.insert(dep.into(), Dynamic::from(state)); } map.insert("after".into(), Dynamic::from_map(deps_map)); Ok(map) } /// Wrapper for zinit_client::start /// /// Starts a service. pub fn zinit_start(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { client::start(socket_path, name).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::stop /// /// Stops a service. pub fn zinit_stop(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { client::stop(socket_path, name).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::restart /// /// Restarts a service. pub fn zinit_restart(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { client::restart(socket_path, name).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::monitor /// /// Starts monitoring a service. pub fn zinit_monitor(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.monitor(name).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::forget /// /// Stops monitoring a service. pub fn zinit_forget(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.forget(name).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::kill /// /// Sends a signal to a service. pub fn zinit_kill(socket_path: &str, name: &str, signal: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.kill(name, signal).await }); result.to_rhai_error()?; Ok(true) } /// Wrapper for zinit_client::create_service /// /// Creates a new service. pub fn zinit_create_service(socket_path: &str, name: &str, exec: &str, oneshot: bool) -> Result> { let rt = get_runtime()?; // Create service configuration let content = serde_json::from_value(json!({ "exec": exec, "oneshot": oneshot })).map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( format!("Failed to create service configuration: {}", e).into(), rhai::Position::NONE )) })?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.create_service(name, content).await }); result.to_rhai_error() } /// Wrapper for zinit_client::delete_service /// /// Deletes a service. pub fn zinit_delete_service(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.delete_service(name).await }); result.to_rhai_error() } /// Wrapper for zinit_client::get_service /// /// Gets a service configuration. pub fn zinit_get_service(socket_path: &str, name: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.get_service(name).await }); let value = result.to_rhai_error()?; // Convert Value to Dynamic match value { Value::Object(map) => { let mut rhai_map = Map::new(); for (k, v) in map { rhai_map.insert(k.into(), value_to_dynamic(v)); } Ok(Dynamic::from_map(rhai_map)) }, _ => Err(Box::new(EvalAltResult::ErrorRuntime( "Expected object from get_service".into(), rhai::Position::NONE ))) } } /// Wrapper for zinit_client::logs with a filter /// /// Gets logs for a specific service. pub fn zinit_logs(socket_path: &str, filter: &str) -> Result> { let rt = get_runtime()?; let filter_string = Some(filter.to_string()); let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.logs(filter_string).await }); let logs = result.to_rhai_error()?; // Convert Vec to Rhai Array let mut array = Array::new(); for log in logs { array.push(Dynamic::from(log)); } Ok(array) } /// Wrapper for zinit_client::logs without a filter /// /// Gets all logs. pub fn zinit_logs_all(socket_path: &str) -> Result> { let rt = get_runtime()?; let result = rt.block_on(async { let client = client::get_zinit_client(socket_path).await?; client.logs(None).await }); let logs = result.to_rhai_error()?; // Convert Vec to Rhai Array let mut array = Array::new(); for log in logs { array.push(Dynamic::from(log)); } Ok(array) } // Helper function to convert serde_json::Value to rhai::Dynamic fn value_to_dynamic(value: Value) -> Dynamic { match value { Value::Null => Dynamic::UNIT, Value::Bool(b) => Dynamic::from(b), Value::Number(n) => { if let Some(i) = n.as_i64() { Dynamic::from(i) } else if let Some(f) = n.as_f64() { Dynamic::from(f) } else { Dynamic::from(n.to_string()) } }, Value::String(s) => Dynamic::from(s), Value::Array(arr) => { let mut rhai_arr = Array::new(); for item in arr { rhai_arr.push(value_to_dynamic(item)); } Dynamic::from(rhai_arr) }, Value::Object(map) => { let mut rhai_map = Map::new(); for (k, v) in map { rhai_map.insert(k.into(), value_to_dynamic(v)); } Dynamic::from_map(rhai_map) } } }