use std::{
collections::HashMap,
path::Path,
sync::{mpsc::channel, Arc, Mutex},
thread,
time::Duration,
};
use chrono::Utc;
use notify::{EventKind, RecursiveMode, Watcher};
use simple_websockets::{Event, Message, Responder};
pub const WS_PORT: u16 = 3001;
const WATCHED_FOLDERS: &[&str] = &["templates", "styles", "public"];
const MIN_RECOMPILE_INTERVAL: u32 = 0;
const FILE_SAVE_WAIT: u64 = 300;
pub fn watch<F>(router: F)
where
F: Fn(),
{
let event_hub = unwrap!(
simple_websockets::launch(WS_PORT),
err: "Failed to initialize websockets on port {} `{err:?}`",
WS_PORT
);
let clients = Arc::new(Mutex::new(HashMap::<u64, Responder>::new()));
let clients_clone = clients.clone();
thread::spawn(move || loop {
let mut clients = clients_clone.lock().unwrap();
for event in event_hub.drain() {
match event {
Event::Connect(id, responder) => {
println!("Connect #{}", id);
clients.insert(id, responder);
}
Event::Disconnect(id) => {
println!("Disconnect #{}", id);
clients.remove(&id);
}
_ => (),
}
}
});
let (tx, rx) = channel();
let mut watcher =
unwrap!(notify::recommended_watcher(tx), err: "Could not create file watcher `{err:?}`");
for folder in WATCHED_FOLDERS {
unwrap!(
watcher.watch(Path::new(folder), RecursiveMode::Recursive),
err: "Could not watch folder '{}' `{err:?}`",
folder
)
}
let mut last_compile = Utc::now().timestamp();
loop {
let Ok(Ok(event)) = rx.recv() else {
continue;
};
if !matches!(
event.kind,
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_)
) {
continue;
}
let now = Utc::now().timestamp();
if last_compile + (MIN_RECOMPILE_INTERVAL as i64) < now {
continue;
}
last_compile = now;
thread::sleep(Duration::from_millis(FILE_SAVE_WAIT));
router();
let clients = clients.lock().unwrap();
for (_id, client) in clients.iter() {
client.send(Message::Text("reload".to_string()));
}
}
}