97 lines
3.2 KiB
Rust
97 lines
3.2 KiB
Rust
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<String>,
|
|
|
|
/// Command to run on change (like: -- run --example server)
|
|
#[arg(last = true)]
|
|
command: Vec<String>,
|
|
}
|
|
|
|
#[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::<Child>));
|
|
|
|
// 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;
|
|
}
|
|
});
|
|
}
|
|
} |