//! # Hero Supervisor Application //! //! Simplified supervisor application that wraps a built Supervisor instance. //! Use SupervisorBuilder to construct the supervisor with all configuration, //! then pass it to SupervisorApp for runtime management. use crate::Supervisor; use crate::mycelium::MyceliumIntegration; use log::{info, error, debug}; use std::sync::Arc; use tokio::sync::Mutex; /// Main supervisor application pub struct SupervisorApp { pub supervisor: Supervisor, pub mycelium_url: String, pub topic: String, } impl SupervisorApp { /// Create a new supervisor application with a built supervisor pub fn new(supervisor: Supervisor, mycelium_url: String, topic: String) -> Self { Self { supervisor, mycelium_url, topic, } } /// Start the complete supervisor application /// This method handles the entire application lifecycle: /// - Starts all configured runners /// - Connects to Mycelium daemon for message transport /// - Sets up graceful shutdown handling /// - Keeps the application running pub async fn start(&mut self) -> Result<(), Box> { info!("Starting Hero Supervisor Application"); // Start all configured runners self.start_all().await?; // Start Mycelium integration self.start_mycelium_integration().await?; // Set up graceful shutdown self.setup_graceful_shutdown().await; // Keep the application running info!("Supervisor is running. Press Ctrl+C to shutdown."); self.run_main_loop().await; Ok(()) } /// Start the Mycelium integration async fn start_mycelium_integration(&self) -> Result<(), Box> { info!("Starting Mycelium integration..."); let supervisor_for_mycelium = Arc::new(Mutex::new(self.supervisor.clone())); let mycelium_url = self.mycelium_url.clone(); let topic = self.topic.clone(); let mycelium_integration = MyceliumIntegration::new( supervisor_for_mycelium, mycelium_url, topic, ); // Start the Mycelium integration in a background task let integration_handle = tokio::spawn(async move { if let Err(e) = mycelium_integration.start().await { error!("Mycelium integration error: {}", e); } }); // Give the integration a moment to start tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; info!("Mycelium integration started successfully"); // Store the handle for potential cleanup std::mem::forget(integration_handle); // For now, let it run in background Ok(()) } /// Set up graceful shutdown handling async fn setup_graceful_shutdown(&self) { tokio::spawn(async move { tokio::signal::ctrl_c().await.expect("Failed to listen for ctrl+c"); info!("Received shutdown signal"); std::process::exit(0); }); } /// Main application loop async fn run_main_loop(&self) { // Keep the main thread alive loop { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } } /// Start all configured runners pub async fn start_all(&mut self) -> Result<(), Box> { info!("Starting all runners"); let results = self.supervisor.start_all().await; let mut failed_count = 0; for (runner_id, result) in results { match result { Ok(_) => info!("Runner {} started successfully", runner_id), Err(e) => { error!("Failed to start runner {}: {}", runner_id, e); failed_count += 1; } } } if failed_count == 0 { info!("All runners started successfully"); } else { error!("Failed to start {} runners", failed_count); } Ok(()) } /// Stop all configured runners pub async fn stop_all(&mut self, force: bool) -> Result<(), Box> { info!("Stopping all runners (force: {})", force); let results = self.supervisor.stop_all(force).await; let mut failed_count = 0; for (runner_id, result) in results { match result { Ok(_) => info!("Runner {} stopped successfully", runner_id), Err(e) => { error!("Failed to stop runner {}: {}", runner_id, e); failed_count += 1; } } } if failed_count == 0 { info!("All runners stopped successfully"); } else { error!("Failed to stop {} runners", failed_count); } Ok(()) } /// Get status of all runners pub async fn get_status(&self) -> Result, Box> { debug!("Getting status of all runners"); let statuses = self.supervisor.get_all_runner_status().await .map_err(|e| Box::new(e) as Box)?; let status_strings: Vec<(String, String)> = statuses .into_iter() .map(|(runner_id, status)| { let status_str = format!("{:?}", status); (runner_id, status_str) }) .collect(); Ok(status_strings) } }