//! Simplified main function for Baobab Actor TUI //! //! This binary provides a clean entry point for the actor monitoring and job dispatch interface. use anyhow::{Result, Context}; use baobab_actor::terminal_ui::{App, setup_and_run_tui}; use clap::Parser; use log::{info, warn, error}; use std::path::PathBuf; use std::process::{Child, Command}; use tokio::signal; #[derive(Parser)] #[command(name = "baobab-actor-tui")] #[command(about = "Terminal UI for Baobab Actor - Monitor and dispatch jobs to a single actor")] struct Args { /// Redis URL for job queue #[arg(short, long, default_value = "redis://localhost:6379")] redis_url: String, /// Enable verbose logging #[arg(short, long)] verbose: bool, } /// Initialize logging based on verbosity level fn init_logging(verbose: bool) { if verbose { env_logger::Builder::from_default_env() .filter_level(log::LevelFilter::Debug) .init(); } else { env_logger::Builder::from_default_env() .filter_level(log::LevelFilter::Info) .init(); } } /// Create and configure the TUI application fn create_app(args: &Args) -> Result { let actor_id = "osis".to_string(); // Get the crate root directory let crate_root = std::env::var("CARGO_MANIFEST_DIR") .unwrap_or_else(|_| ".".to_string()); let crate_root = PathBuf::from(crate_root); let actor_path = crate_root.join("target/debug/actor_osis"); let example_dir = Some(crate_root.join("examples/scripts")); App::new( actor_id, actor_path, args.redis_url.clone(), example_dir, ) } /// Spawn the actor binary as a background process fn spawn_actor_process(_args: &Args) -> Result { // Get the crate root directory let crate_root = std::env::var("CARGO_MANIFEST_DIR") .unwrap_or_else(|_| ".".to_string()); let actor_path = PathBuf::from(crate_root).join("target/debug/actor_osis"); info!("๐ŸŽฌ Spawning actor process: {}", actor_path.display()); let mut cmd = Command::new(&actor_path); // Redirect stdout and stderr to null to prevent logs from interfering with TUI cmd.stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()); // Spawn the process let child = cmd .spawn() .with_context(|| format!("Failed to spawn actor process: {}", actor_path.display()))?; info!("โœ… Actor process spawned with PID: {}", child.id()); Ok(child) } /// Cleanup function to terminate actor process fn cleanup_actor_process(mut actor_process: Child) { info!("๐Ÿงน Cleaning up actor process..."); match actor_process.try_wait() { Ok(Some(status)) => { info!("Actor process already exited with status: {}", status); } Ok(None) => { info!("Terminating actor process..."); if let Err(e) = actor_process.kill() { error!("Failed to kill actor process: {}", e); } else { match actor_process.wait() { Ok(status) => info!("Actor process terminated with status: {}", status), Err(e) => error!("Failed to wait for actor process: {}", e), } } } Err(e) => { error!("Failed to check actor process status: {}", e); } } } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); // Initialize logging init_logging(args.verbose); let crate_root = std::env::var("CARGO_MANIFEST_DIR") .unwrap_or_else(|_| ".".to_string()); info!("๐Ÿš€ Starting Baobab Actor TUI..."); info!("Actor ID: osis"); info!("Actor Path: {}/target/debug/actor_osis", crate_root); info!("Redis URL: {}", args.redis_url); info!("Example Directory: {}/examples/scripts", crate_root); // Spawn the actor process first let actor_process = spawn_actor_process(&args)?; // Give the actor a moment to start up tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; // Create app and run TUI let app = create_app(&args)?; // Set up signal handling for graceful shutdown let result = tokio::select! { tui_result = setup_and_run_tui(app) => { info!("TUI exited"); tui_result } _ = signal::ctrl_c() => { info!("Received Ctrl+C, shutting down..."); Ok(()) } }; // Clean up the actor process cleanup_actor_process(actor_process); result }