use crate::{ServiceConfig, ServiceManager, ServiceManagerError, ServiceStatus}; use async_trait::async_trait; use serde_json::json; use std::sync::Arc; use zinit_client::{get_zinit_client, ServiceStatus as ZinitServiceStatus, ZinitClientWrapper}; pub struct ZinitServiceManager { client: Arc, } impl ZinitServiceManager { pub fn new(socket_path: &str) -> Result { // This is a blocking call to get the async client. // We might want to make this async in the future if the constructor can be async. let client = tokio::runtime::Runtime::new() .unwrap() .block_on(get_zinit_client(socket_path)) .map_err(|e| ServiceManagerError::Other(e.to_string()))?; Ok(ZinitServiceManager { client }) } } #[async_trait] impl ServiceManager for ZinitServiceManager { fn exists(&self, service_name: &str) -> Result { let status_res = self.status(service_name); match status_res { Ok(_) => Ok(true), Err(ServiceManagerError::ServiceNotFound(_)) => Ok(false), Err(e) => Err(e), } } fn start(&self, config: &ServiceConfig) -> Result<(), ServiceManagerError> { let service_config = json!({ "exec": config.binary_path, "args": config.args, "working_directory": config.working_directory, "env": config.environment, "restart": config.auto_restart, }); tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.create_service(&config.name, service_config)) .map_err(|e| ServiceManagerError::StartFailed(config.name.clone(), e.to_string()))?; self.start_existing(&config.name) } fn start_existing(&self, service_name: &str) -> Result<(), ServiceManagerError> { tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.start(service_name)) .map_err(|e| ServiceManagerError::StartFailed(service_name.to_string(), e.to_string())) } async fn start_and_confirm(&self, config: &ServiceConfig, _timeout_secs: u64) -> Result<(), ServiceManagerError> { self.start(config) } async fn run(&self, config: &ServiceConfig, _timeout_secs: u64) -> Result<(), ServiceManagerError> { self.start(config) } async fn start_existing_and_confirm(&self, service_name: &str, _timeout_secs: u64) -> Result<(), ServiceManagerError> { self.start_existing(service_name) } fn stop(&self, service_name: &str) -> Result<(), ServiceManagerError> { tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.stop(service_name)) .map_err(|e| ServiceManagerError::StopFailed(service_name.to_string(), e.to_string())) } fn restart(&self, service_name: &str) -> Result<(), ServiceManagerError> { tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.restart(service_name)) .map_err(|e| ServiceManagerError::RestartFailed(service_name.to_string(), e.to_string())) } fn status(&self, service_name: &str) -> Result { let status: ZinitServiceStatus = tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.status(service_name)) .map_err(|e| ServiceManagerError::Other(e.to_string()))?; let service_status = match status { ZinitServiceStatus::Running(_) => crate::ServiceStatus::Running, ZinitServiceStatus::Stopped => crate::ServiceStatus::Stopped, ZinitServiceStatus::Failed(_) => crate::ServiceStatus::Failed, ZinitServiceStatus::Waiting(_) => crate::ServiceStatus::Unknown, }; Ok(service_status) } fn logs(&self, service_name: &str, _lines: Option) -> Result { let logs = tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.logs(Some(service_name.to_string()))) .map_err(|e| ServiceManagerError::LogsFailed(service_name.to_string(), e.to_string()))?; Ok(logs.join("\n")) } fn list(&self) -> Result, ServiceManagerError> { let services = tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.list()) .map_err(|e| ServiceManagerError::Other(e.to_string()))?; Ok(services.keys().cloned().collect()) } fn remove(&self, service_name: &str) -> Result<(), ServiceManagerError> { let _ = self.stop(service_name); // Best effort to stop before removing tokio::runtime::Runtime::new() .unwrap() .block_on(self.client.delete_service(service_name)) .map_err(|e| ServiceManagerError::Other(e.to_string())) } }