initial commit

This commit is contained in:
Timur Gordon
2025-08-26 14:49:21 +02:00
commit 767c66fb6a
66 changed files with 22035 additions and 0 deletions

166
src/app.rs Normal file
View File

@@ -0,0 +1,166 @@
//! # 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::openrpc::start_openrpc_servers;
use log::{info, error, debug};
use std::sync::Arc;
use tokio::sync::Mutex;
/// Main supervisor application
pub struct SupervisorApp {
pub supervisor: Supervisor,
pub bind_address: String,
pub port: u16,
}
impl SupervisorApp {
/// Create a new supervisor application with a built supervisor
pub fn new(supervisor: Supervisor, bind_address: String, port: u16) -> Self {
Self {
supervisor,
bind_address,
port,
}
}
/// Start the complete supervisor application
/// This method handles the entire application lifecycle:
/// - Starts all configured runners
/// - Launches the OpenRPC server
/// - Sets up graceful shutdown handling
/// - Keeps the application running
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
info!("Starting Hero Supervisor Application");
// Start all configured runners
self.start_all().await?;
// Start OpenRPC server
self.start_openrpc_server().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 OpenRPC server
async fn start_openrpc_server(&self) -> Result<(), Box<dyn std::error::Error>> {
info!("Starting OpenRPC server...");
let supervisor_for_openrpc = Arc::new(Mutex::new(self.supervisor.clone()));
let bind_address = self.bind_address.clone();
let port = self.port;
// Start the OpenRPC server in a background task
let server_handle = tokio::spawn(async move {
if let Err(e) = start_openrpc_servers(supervisor_for_openrpc, &bind_address, port).await {
error!("OpenRPC server error: {}", e);
}
});
// Give the server a moment to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
info!("OpenRPC server started successfully");
// Store the handle for potential cleanup (we could add this to the struct if needed)
std::mem::forget(server_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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<Vec<(String, String)>, Box<dyn std::error::Error>> {
debug!("Getting status of all runners");
let statuses = self.supervisor.get_all_runner_status().await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
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)
}
}