use axum::Router;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::net::SocketAddr;
use std::path::Path;
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use tokio::sync::oneshot;
use tower_http::services::ServeDir;
use crate::error::Result;
pub async fn execute() -> Result<()> {
println!("Running in development mode...");
start_watch().await;
Ok(())
}
pub async fn start_watch() {
let watch_path = "./content";
println!("Starting hot reload server, watching: {}", watch_path);
println!("Press Ctrl+C to stop");
let (file_tx, file_rx) = channel();
let mut watcher = RecommendedWatcher::new(file_tx, Config::default()).unwrap();
watcher
.watch(Path::new(watch_path), RecursiveMode::Recursive)
.unwrap();
let (shutdown_tx, _) = oneshot::channel::<()>();
let shutdown_tx = Arc::new(Mutex::new(Some(shutdown_tx)));
let mut server_handle = start_server(Arc::clone(&shutdown_tx));
let mut last_restart = Instant::now();
loop {
match file_rx.recv_timeout(Duration::from_millis(100)) {
Ok(_) => {
if last_restart.elapsed() > Duration::from_millis(500) {
println!("File change detected. Restarting server...");
if let Some(tx) = shutdown_tx.lock().unwrap().take() {
let _ = tx.send(());
}
if let Some(handle) = server_handle.take() {
let _ = handle.join();
}
let (new_tx, _) = oneshot::channel::<()>();
*shutdown_tx.lock().unwrap() = Some(new_tx);
server_handle = start_server(Arc::clone(&shutdown_tx));
last_restart = Instant::now();
}
}
Err(_) => {
}
}
}
}
fn start_server(
shutdown_tx: Arc<Mutex<Option<oneshot::Sender<()>>>>,
) -> Option<thread::JoinHandle<()>> {
println!("Starting server...");
let handle = thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let service = ServeDir::new("content");
let app = Router::new().fallback_service(service);
let (tx, rx) = oneshot::channel::<()>();
*shutdown_tx.lock().unwrap() = Some(tx);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Server started at http://{}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(async {
rx.await.ok();
println!("Server shutting down gracefully");
})
.await
.unwrap();
});
});
Some(handle)
}