use clap::{Parser}; use notify::{RecursiveMode, Watcher, EventKind, recommended_watcher}; use std::sync::{Arc, Mutex}; use std::process::{Command, Child, Stdio}; use tokio::net::TcpListener; use tokio::sync::broadcast; use tokio_tungstenite::accept_async; use futures_util::{SinkExt, StreamExt}; #[derive(Parser, Debug)] #[command(author, version, about)] struct Args { /// Paths to watch (like src/, templates/, scripts/) #[arg(short, long, value_name = "PATH", num_args = 1.., required = true)] watch: Vec, /// Command to run on change (like: -- run --example server) #[arg(last = true)] command: Vec, } #[tokio::main] async fn main() { let args = Args::parse(); let (tx, _) = broadcast::channel::<()>(10); let tx_ws = tx.clone(); let server_process = Arc::new(Mutex::new(None::)); // Start WebSocket reload server tokio::spawn(start_websocket_server(tx_ws.clone())); // Start watching files and restarting command let server_process_clone = Arc::clone(&server_process); let watch_paths = args.watch.clone(); let cmd_args = args.command.clone(); std::thread::spawn(move || { let mut watcher = recommended_watcher(move |res| { if let Ok(event) = res { if matches!(event.kind, EventKind::Modify(_) | EventKind::Create(_) | EventKind::Remove(_)) { println!("📦 Change detected, restarting..."); // Kill previous process if let Some(mut child) = server_process_clone.lock().unwrap().take() { let _ = child.kill(); } // Run new process let mut cmd = Command::new("cargo"); cmd.args(&cmd_args); cmd.stdout(Stdio::inherit()) .stderr(Stdio::inherit()); let child = cmd.spawn().expect("Failed to spawn"); *server_process_clone.lock().unwrap() = Some(child); // Notify browser let _ = tx.send(()); } } }).expect("watcher failed"); // Add watches for path in &watch_paths { watcher.watch(path, RecursiveMode::Recursive).unwrap(); } loop { std::thread::sleep(std::time::Duration::from_secs(3600)); } }); println!("🔁 Watching paths: {:?}", args.watch); println!("🌐 Connect browser to ws://localhost:35729"); loop { tokio::time::sleep(tokio::time::Duration::from_secs(3600)).await; } } async fn start_websocket_server(tx: broadcast::Sender<()>) { let listener = TcpListener::bind("127.0.0.1:35729").await.unwrap(); while let Ok((stream, _)) = listener.accept().await { let tx = tx.clone(); let mut rx = tx.subscribe(); tokio::spawn(async move { let ws_stream = accept_async(stream).await.unwrap(); let (mut write, _) = ws_stream.split(); while rx.recv().await.is_ok() { let _ = write.send(tokio_tungstenite::tungstenite::Message::Text("reload".into())).await; } }); } }