//! Rhai integration for the service manager module //! //! This module provides Rhai scripting support for service management operations. use crate::{create_service_manager, ServiceConfig, ServiceManager}; use rhai::{Engine, EvalAltResult, Map}; use std::collections::HashMap; use std::sync::Arc; /// A wrapper around ServiceManager that can be used in Rhai #[derive(Clone)] pub struct RhaiServiceManager { inner: Arc>, } impl RhaiServiceManager { pub fn new() -> Self { Self { inner: Arc::new(create_service_manager()), } } } /// Register the service manager module with a Rhai engine pub fn register_service_manager_module(engine: &mut Engine) -> Result<(), Box> { // Factory function to create service manager engine.register_type::(); engine.register_fn("create_service_manager", RhaiServiceManager::new); // Service management functions engine.register_fn( "start", |manager: &mut RhaiServiceManager, config: Map| -> Result<(), Box> { let service_config = map_to_service_config(config)?; manager .inner .start(&service_config) .map_err(|e| format!("Failed to start service: {}", e).into()) }, ); engine.register_fn( "stop", |manager: &mut RhaiServiceManager, service_name: String| -> Result<(), Box> { manager .inner .stop(&service_name) .map_err(|e| format!("Failed to stop service: {}", e).into()) }, ); engine.register_fn( "restart", |manager: &mut RhaiServiceManager, service_name: String| -> Result<(), Box> { manager .inner .restart(&service_name) .map_err(|e| format!("Failed to restart service: {}", e).into()) }, ); engine.register_fn( "status", |manager: &mut RhaiServiceManager, service_name: String| -> Result> { let status = manager .inner .status(&service_name) .map_err(|e| format!("Failed to get service status: {}", e))?; Ok(format!("{:?}", status)) }, ); engine.register_fn( "logs", |manager: &mut RhaiServiceManager, service_name: String, lines: i64| -> Result> { let lines_opt = if lines > 0 { Some(lines as usize) } else { None }; manager .inner .logs(&service_name, lines_opt) .map_err(|e| format!("Failed to get service logs: {}", e).into()) }, ); engine.register_fn( "list", |manager: &mut RhaiServiceManager| -> Result, Box> { manager .inner .list() .map_err(|e| format!("Failed to list services: {}", e).into()) }, ); engine.register_fn( "remove", |manager: &mut RhaiServiceManager, service_name: String| -> Result<(), Box> { manager .inner .remove(&service_name) .map_err(|e| format!("Failed to remove service: {}", e).into()) }, ); engine.register_fn( "exists", |manager: &mut RhaiServiceManager, service_name: String| -> Result> { manager .inner .exists(&service_name) .map_err(|e| format!("Failed to check if service exists: {}", e).into()) }, ); engine.register_fn( "start_and_confirm", |manager: &mut RhaiServiceManager, config: Map, timeout_secs: i64| -> Result<(), Box> { let service_config = map_to_service_config(config)?; let timeout = if timeout_secs > 0 { timeout_secs as u64 } else { 30 }; manager .inner .start_and_confirm(&service_config, timeout) .map_err(|e| format!("Failed to start and confirm service: {}", e).into()) }, ); engine.register_fn( "start_existing_and_confirm", |manager: &mut RhaiServiceManager, service_name: String, timeout_secs: i64| -> Result<(), Box> { let timeout = if timeout_secs > 0 { timeout_secs as u64 } else { 30 }; manager .inner .start_existing_and_confirm(&service_name, timeout) .map_err(|e| format!("Failed to start existing service and confirm: {}", e).into()) }, ); Ok(()) } /// Convert a Rhai Map to a ServiceConfig fn map_to_service_config(map: Map) -> Result> { let name = map .get("name") .and_then(|v| v.clone().into_string().ok()) .ok_or("Service config must have a 'name' field")?; let binary_path = map .get("binary_path") .and_then(|v| v.clone().into_string().ok()) .ok_or("Service config must have a 'binary_path' field")?; let args = map .get("args") .and_then(|v| v.clone().try_cast::()) .map(|arr| { arr.into_iter() .filter_map(|v| v.into_string().ok()) .collect::>() }) .unwrap_or_default(); let working_directory = map .get("working_directory") .and_then(|v| v.clone().into_string().ok()); let environment = map .get("environment") .and_then(|v| v.clone().try_cast::()) .map(|env_map| { env_map .into_iter() .filter_map(|(k, v)| v.into_string().ok().map(|val| (k.to_string(), val))) .collect::>() }) .unwrap_or_default(); let auto_restart = map .get("auto_restart") .and_then(|v| v.as_bool().ok()) .unwrap_or(false); Ok(ServiceConfig { name, binary_path, args, working_directory, environment, auto_restart, }) } #[cfg(test)] mod tests { use super::*; use rhai::{Engine, Map}; #[test] fn test_register_service_manager_module() { let mut engine = Engine::new(); register_service_manager_module(&mut engine).unwrap(); // Test that the functions are registered // Note: Rhai doesn't expose a public API to check if functions are registered // So we'll just verify the module registration doesn't panic assert!(true); } #[test] fn test_map_to_service_config() { let mut map = Map::new(); map.insert("name".into(), "test-service".into()); map.insert("binary_path".into(), "/bin/echo".into()); map.insert("auto_restart".into(), true.into()); let config = map_to_service_config(map).unwrap(); assert_eq!(config.name, "test-service"); assert_eq!(config.binary_path, "/bin/echo"); assert_eq!(config.auto_restart, true); } }