livereload_server/
lib.rs

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}