//! Terminal UI for Sync Actor - Monitor and dispatch jobs to sync actor with DSL modules //! //! This binary provides a TUI interface for monitoring and dispatching jobs to the sync actor. use anyhow::{Result, Context}; use clap::Parser; use log::{info, warn, error}; use std::path::PathBuf; use std::process::{Child, Command}; use tokio::signal; #[derive(Parser)] #[command(name = "sync-actor-tui")] #[command(about = "Terminal UI for Sync Actor - Monitor and dispatch jobs with DSL modules")] struct Args { /// Actor ID for this instance #[arg(short, long, default_value = "sync_osis")] actor_id: String, /// Redis URL for job queue #[arg(short, long, default_value = "redis://localhost:6379")] redis_url: String, /// Database path #[arg(short, long, default_value = "/tmp/actor_db")] db_path: String, /// Preserve completed tasks in Redis (don't delete them) #[arg(short, long, default_value = "false")] preserve_tasks: bool, /// 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(); } } /// Spawn the sync 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/sync_actor"); info!("๐ŸŽฌ Spawning sync actor process: {}", actor_path.display()); let mut cmd = Command::new(&actor_path); // Add command line arguments cmd.args(&[ "--actor-id", &args.actor_id, "--db-path", &args.db_path, "--redis-url", &args.redis_url, "--preserve-tasks", &args.preserve_tasks.to_string(), ]); // 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 sync actor process: {}", actor_path.display()))?; info!("โœ… Sync 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 sync actor process..."); match actor_process.try_wait() { Ok(Some(status)) => { info!("Sync actor process already exited with status: {}", status); } Ok(None) => { info!("Terminating sync actor process..."); if let Err(e) = actor_process.kill() { error!("Failed to kill sync actor process: {}", e); } else { match actor_process.wait() { Ok(status) => info!("Sync actor process terminated with status: {}", status), Err(e) => error!("Failed to wait for sync actor process: {}", e), } } } Err(e) => { error!("Failed to check sync 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 Sync Actor TUI..."); info!("Actor ID: {}", args.actor_id); info!("Actor Path: {}/target/debug/sync_actor", crate_root); info!("Redis URL: {}", args.redis_url); info!("Database Path: {}", args.db_path); info!("Preserve Tasks: {}", args.preserve_tasks); info!("Script Type: DSL (Domain Specific Language)"); // 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; info!("๐Ÿ“‹ Sync Actor TUI is running. The actor processes jobs sequentially."); info!("๐Ÿ’ก Use Redis CLI or job submission tools to send jobs to queue: actor_queue:{}", args.actor_id); info!("๐Ÿ”„ Jobs will be processed with DSL modules (business operations)"); info!("๐Ÿ“Š Jobs are processed one at a time in order"); if args.preserve_tasks { info!("๐Ÿ’พ Completed tasks will be preserved in Redis"); } else { info!("๐Ÿ—‘๏ธ Completed tasks will be cleaned up from Redis"); } info!("Press Ctrl+C to exit..."); // Wait for Ctrl+C let result = tokio::select! { _ = signal::ctrl_c() => { info!("Received Ctrl+C, shutting down..."); Ok(()) } }; // Clean up the actor process cleanup_actor_process(actor_process); result }