1use std::path::Path;
2
3use futures_util::{SinkExt, StreamExt};
4use lazy_static::lazy_static;
5use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
6use tokio::sync::mpsc;
7use warp::ws::Message;
8
9const INJECTED_SCRIPT: &str = include_str!("./injected_script.js");
10const NOTIFY_CHANNEL_CAPACITY: usize = 5;
11
12lazy_static! {
13 static ref INJECTION_PAYLOAD: String = {
14 format!(r#"<script type="text/javascript">{INJECTED_SCRIPT}</script>"#)
15 };
16}
17
18mod inject;
19mod serve;
20
21pub use self::serve::serve_file;
22
23pub fn async_watcher(
24) -> notify::Result<(RecommendedWatcher, mpsc::Receiver<notify::Result<Event>>)>
25{
26 let (notify_event_tx, notify_event_rx) =
27 mpsc::channel(NOTIFY_CHANNEL_CAPACITY);
28
29 let watcher = RecommendedWatcher::new(
30 move |res| {
31 match notify_event_tx.try_send(res) {
32 Ok(()) => (),
33 Err(err) => log::error!("Failed to send an event to channel, the channel is likely closed: {err}"),
34 }
35 },
36 Config::default(),
37 )?;
38
39 Ok((watcher, notify_event_rx))
40}
41
42pub async fn handle_websocket(
43 target_dir_path: impl AsRef<Path>,
44 websocket: warp::ws::WebSocket,
45) -> anyhow::Result<()> {
46 let (mut watcher, mut fs_events_receiver) = async_watcher()?;
47
48 let target_dir_path = target_dir_path.as_ref();
49
50 watcher.watch(target_dir_path, RecursiveMode::Recursive)?;
51
52 let (mut websocket_tx, mut websocket_rx) = websocket.split();
53
54 loop {
55 tokio::select! {
56 Some(Ok(_)) = fs_events_receiver.recv() => {
57 let s = websocket_tx.send(Message::text("reload"));
58
59 s.await?;
60 }
61 Some(message) = websocket_rx.next() => {
62 match message {
63 Ok(msg) => {
64 if msg.is_close() {
65 log::debug!("Websocket connection is closed");
66 break;
67 }
68 }
69 Err(err) => {
70 log::error!("An error occured: {err}");
71 break;
72 }
73 }
74 }
75 }
76 }
77
78 Ok(())
79}