use std::path::{Path, PathBuf}; use std::{env, io}; use axum::body::Body; use axum::http::Request; use listenfd::ListenFd; use notify::Watcher; use thiserror::Error; use tokio::net::TcpListener; use tower_livereload::predicate::Predicate; use tower_livereload::LiveReloadLayer; use ::app::get_router; #[derive(Error, Debug)] pub enum AppError { #[error("Unable to bind to TCP listener")] TCPListener(#[from] std::io::Error), #[error("Error with the notify watcher")] NotifyWatcher(#[from] notify::Error), #[error("Missing environment variable {var}")] MissingEnvVar { var: &'static str }, } /// Nous filtrons les requêtes de `htmx` pour ne pas inclure le script _JS_ qui gère le rechargement /// Voir https://github.com/leotaku/tower-livereload/pull/3 #[derive(Copy, Clone)] struct NotHtmxPredicate; impl Predicate> for NotHtmxPredicate { fn check(&mut self, req: &Request) -> bool { !(req.headers().contains_key("hx-request")) } } const DEFAULT_LISTENER: &str = "localhost:3000"; async fn get_tcp_listener() -> Result { let mut listenfd = ListenFd::from_env(); match listenfd.take_tcp_listener(0)? { // if we are given a tcp listener on listen fd 0, we use that one Some(listener) => { listener.set_nonblocking(true)?; Ok(TcpListener::from_std(listener)?) } // otherwise fall back to local listening None => Ok(TcpListener::bind(DEFAULT_LISTENER).await?), } } fn get_livereload_layer( templates_paths: Vec, ) -> Result, notify::Error> { let livereload = LiveReloadLayer::new(); let reloader = livereload.reloader(); let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?; for templates_path in templates_paths { watcher.watch(templates_path.as_path(), notify::RecursiveMode::Recursive)?; } Ok(livereload.request_predicate::(NotHtmxPredicate)) } #[tokio::main] async fn main() -> Result<(), AppError> { let manifest_dir = env::var("CARGO_MANIFEST_DIR").map_err(|_| AppError::MissingEnvVar { var: "CARGO_MANIFEST_DIR", })?; let assets_path = Path::new(&manifest_dir).join("assets"); let templates_paths = vec![ Path::new(&manifest_dir).join("src/pages"), Path::new(&manifest_dir).join("src/components"), ]; let livereload_layer = get_livereload_layer(templates_paths).map_err(AppError::NotifyWatcher)?; let router = get_router(assets_path.as_path()).layer(livereload_layer); let listener: TcpListener = get_tcp_listener().await.map_err(AppError::TCPListener)?; let local_addr = listener.local_addr().map_err(AppError::TCPListener)?; println!("Listening on: http://{}", local_addr); // Run the server with the router axum::serve(listener, router.into_make_service()).await?; Ok(()) }