live_server/
lib.rs

1//! Launch a local network server with live reload feature for static pages.
2//!
3//! ## Create live server
4//! ```
5//! use live_server::{listen, Options};
6//!
7//! async fn serve() -> Result<(), Box<dyn std::error::Error>> {
8//!     listen("127.0.0.1:8080", "./").await?.start(Options::default()).await
9//! }
10//! ```
11//!
12//! ## Enable logs (Optional)
13//! ```rust
14//! env_logger::init();
15//! ```
16
17mod file_layer;
18mod http_layer;
19mod utils;
20
21pub use http_layer::server::Options;
22
23use file_layer::watcher::{create_watcher, watch};
24use http_layer::{
25    listener::create_listener,
26    server::{create_server, serve, AppState},
27};
28use local_ip_address::local_ip;
29use notify::RecommendedWatcher;
30use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache};
31use std::{
32    error::Error,
33    net::IpAddr,
34    path::{Path, PathBuf},
35    sync::Arc,
36};
37use tokio::{
38    net::TcpListener,
39    sync::{broadcast, mpsc::Receiver},
40};
41
42pub struct Listener {
43    tcp_listener: TcpListener,
44    root_path: PathBuf,
45    debouncer: Debouncer<RecommendedWatcher, RecommendedCache>,
46    rx: Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>,
47}
48
49impl Listener {
50    /// Start live-server.
51    ///
52    /// ```
53    /// use live_server::{listen, Options};
54    ///
55    /// async fn serve() -> Result<(), Box<dyn std::error::Error>> {
56    ///     listen("127.0.0.1:8080", "./").await?.start(Options::default()).await
57    /// }
58    /// ```
59    pub async fn start(self, options: Options) -> Result<(), Box<dyn Error>> {
60        let (tx, _) = broadcast::channel(16);
61
62        let arc_tx = Arc::new(tx);
63        let app_state = AppState {
64            hard_reload: options.hard_reload,
65            index_listing: options.index_listing,
66            auto_ignore: options.auto_ignore,
67            tx: arc_tx.clone(),
68            root: self.root_path.clone(),
69        };
70
71        let watcher_future = tokio::spawn(watch(
72            self.root_path,
73            self.debouncer,
74            self.rx,
75            arc_tx,
76            options.auto_ignore,
77        ));
78        let server_future = tokio::spawn(serve(self.tcp_listener, create_server(app_state)));
79
80        tokio::try_join!(watcher_future, server_future)?;
81
82        Ok(())
83    }
84
85    /// Return the link of the server, like `http://127.0.0.1:8080`.
86    ///
87    /// ```
88    /// use live_server::{listen, Options};
89    ///
90    /// async fn serve() {
91    ///     let listener = listen("127.0.0.1:8080", "./").await.unwrap();
92    ///     let link = listener.link().unwrap();
93    ///     assert_eq!(link, "http://127.0.0.1:8080");
94    /// }
95    /// ```
96    ///
97    /// This is useful when you did not specify the host or port (e.g. `listen("0.0.0.0:0", ".")`),
98    /// because this method will return the specific address.
99    pub fn link(&self) -> Result<String, Box<dyn Error>> {
100        let addr = self.tcp_listener.local_addr()?;
101        let port = addr.port();
102        let host = addr.ip();
103        let host = match host.is_unspecified() {
104            true => local_ip()?,
105            false => host,
106        };
107
108        Ok(match host {
109            IpAddr::V4(host) => format!("http://{host}:{port}"),
110            IpAddr::V6(host) => format!("http://[{host}]:{port}"),
111        })
112    }
113}
114
115/// Create live-server listener
116///
117/// ```
118/// use live_server::{listen, Options};
119///
120/// async fn serve() -> Result<(), Box<dyn std::error::Error>> {
121///     listen("127.0.0.1:8080", "./").await?.start(Options::default()).await
122/// }
123/// ```
124pub async fn listen(addr: impl AsRef<str>, root: impl AsRef<Path>) -> Result<Listener, String> {
125    let tcp_listener = create_listener(addr.as_ref()).await?;
126    let (debouncer, root_path, rx) = create_watcher(root.as_ref()).await?;
127
128    Ok(Listener {
129        tcp_listener,
130        debouncer,
131        root_path,
132        rx,
133    })
134}