1mod 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 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 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
115pub 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}