feat: [app] Use thiserror to properly handle errors instead of unwrap

This commit is contained in:
Florian Briand 2024-08-14 10:40:41 +02:00
parent 5269dd7789
commit d44c561427
Signed by: florian_briand
GPG Key ID: CC981B9E6B98E70B
2 changed files with 35 additions and 20 deletions

View File

@ -10,6 +10,7 @@ axum = "0.7.5"
listenfd = "1.0.1" listenfd = "1.0.1"
notify = "6.1.1" notify = "6.1.1"
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
thiserror = "1.0.63"
tokio = { version = "1.39.1", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.39.1", features = ["macros", "rt-multi-thread"] }
tower-http = { version = "0.5.2", features = ["fs"] } tower-http = { version = "0.5.2", features = ["fs"] }
tower-livereload = "0.9.3" tower-livereload = "0.9.3"

View File

@ -3,12 +3,23 @@ use axum::body::Body;
use axum::http::Request; use axum::http::Request;
use listenfd::ListenFd; use listenfd::ListenFd;
use notify::Watcher; use notify::Watcher;
use std::env;
use std::path::Path; use std::path::Path;
use std::{env, io};
use thiserror::Error;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_livereload::predicate::Predicate; use tower_livereload::predicate::Predicate;
use tower_livereload::LiveReloadLayer; use tower_livereload::LiveReloadLayer;
#[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 /// 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 /// Voir https://github.com/leotaku/tower-livereload/pull/3
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -20,44 +31,47 @@ impl<T> Predicate<Request<T>> for NotHtmxPredicate {
} }
const DEFAULT_LISTENER: &str = "localhost:3000"; const DEFAULT_LISTENER: &str = "localhost:3000";
async fn get_tcp_listener() -> TcpListener { async fn get_tcp_listener() -> Result<TcpListener, io::Error> {
let mut listenfd = ListenFd::from_env(); let mut listenfd = ListenFd::from_env();
match listenfd.take_tcp_listener(0).unwrap() { match listenfd.take_tcp_listener(0)? {
// if we are given a tcp listener on listen fd 0, we use that one // if we are given a tcp listener on listen fd 0, we use that one
Some(listener) => { Some(listener) => {
listener.set_nonblocking(true).unwrap(); listener.set_nonblocking(true)?;
TcpListener::from_std(listener).unwrap() Ok(TcpListener::from_std(listener)?)
} }
// otherwise fall back to local listening // otherwise fall back to local listening
None => TcpListener::bind(DEFAULT_LISTENER).await.unwrap(), None => Ok(TcpListener::bind(DEFAULT_LISTENER).await?),
} }
} }
fn get_livereload_layer(templates_path: &Path) -> LiveReloadLayer<NotHtmxPredicate> { fn get_livereload_layer(
templates_path: &Path,
) -> Result<LiveReloadLayer<NotHtmxPredicate>, notify::Error> {
let livereload = LiveReloadLayer::new(); let livereload = LiveReloadLayer::new();
let reloader = livereload.reloader(); let reloader = livereload.reloader();
let mut watcher = notify::recommended_watcher(move |_| reloader.reload()).unwrap(); let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?;
watcher watcher.watch(templates_path, notify::RecursiveMode::Recursive)?;
.watch(templates_path, notify::RecursiveMode::Recursive) Ok(livereload.request_predicate::<Body, NotHtmxPredicate>(NotHtmxPredicate))
.unwrap();
livereload.request_predicate::<Body, NotHtmxPredicate>(NotHtmxPredicate)
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), AppError> {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 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 assets_path = Path::new(&manifest_dir).join("assets");
let templates_path = Path::new(&manifest_dir).join("templates"); let templates_path = Path::new(&manifest_dir).join("templates");
let livereload_layer = get_livereload_layer(&templates_path); let livereload_layer =
get_livereload_layer(&templates_path).map_err(AppError::NotifyWatcher)?;
let router = get_router(assets_path.as_path()).layer(livereload_layer); let router = get_router(assets_path.as_path()).layer(livereload_layer);
let listener: TcpListener = get_tcp_listener().await; let listener: TcpListener = get_tcp_listener().await.map_err(AppError::TCPListener)?;
println!("Listening on: http://{}", listener.local_addr().unwrap()); let local_addr = listener.local_addr().map_err(AppError::TCPListener)?;
println!("Listening on: http://{}", local_addr);
// Run the server with the router // Run the server with the router
axum::serve(listener, router.into_make_service()) axum::serve(listener, router.into_make_service()).await?;
.await Ok(())
.unwrap();
} }