From ad3a2359825855955c1ec06e8659a8ba62b0f34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Mon, 27 Oct 2025 10:46:22 +0100 Subject: [PATCH] better error handling using anyhow --- Cargo.lock | 7 ++++ Cargo.toml | 1 + foods.db | Bin 12288 -> 12288 bytes src/main.rs | 109 ++++++++++++++++++++++++++++------------------------ 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed74a88..98d877b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "askama" version = "0.14.0" @@ -812,6 +818,7 @@ checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" name = "task_counter" version = "0.1.0" dependencies = [ + "anyhow", "askama", "axum", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 9cbee44..63257d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.100" askama = "0.14.0" axum = "0.8.6" chrono = "0.4.42" diff --git a/foods.db b/foods.db index 9f2a85377b5944fb2e6566f7ce295e8f226fc6cb..10e67b16840b68bae1ebcc777463ba475786a5ca 100644 GIT binary patch delta 51 zcmZojXh@hK%~&~6#+k8lW5P;##)F%=6gKnnvNJHayD{=GNZT?pa!x)VuR8gmw$x@$ Hz3+?wks=Pu delta 35 rcmZojXh@hK%~&x}#+k8VW5P;##sizV6gKlRvQ6Hg?Y)^(?>i#^-scP_ diff --git a/src/main.rs b/src/main.rs index 20d450e..f5000fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use axum::{ Router, extract::{MatchedPath, Path, State}, http::{Request, StatusCode}, - response::Html, + response::{Html, IntoResponse, Response}, routing::{get, post}, }; use parking_lot::Mutex; @@ -80,7 +80,7 @@ impl Food { struct PreparedStatements {} impl<'conn> PreparedStatements { - fn check(conn: &Connection) -> rusqlite::Result { + fn check(conn: &Connection) -> anyhow::Result { conn.prepare_cached(include_str!("increase.sql"))?; conn.prepare_cached(include_str!("decrease.sql"))?; conn.prepare_cached(include_str!("get_food.sql"))?; @@ -121,10 +121,35 @@ impl<'conn> PreparedStatements { } } +// Make our own error that wraps `anyhow::Error`. +struct AppError(anyhow::Error); + +// Tell axum how to convert `AppError` into a response. +impl IntoResponse for AppError { + fn into_response(self) -> Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} + type ConnState = Arc>; -fn get_version(tx: &Transaction) -> rusqlite::Result { - tx.query_one(include_str!("get_version.sql"), (), |row| row.get(0)) +fn get_version(tx: &Transaction) -> anyhow::Result { + Ok(tx.query_one(include_str!("get_version.sql"), (), |row| row.get(0))?) } fn get_migrations() -> [&'static str; 4] { @@ -136,7 +161,7 @@ fn get_migrations() -> [&'static str; 4] { ] } -fn do_migrations(conn: &mut Connection) -> rusqlite::Result<()> { +fn do_migrations(conn: &mut Connection) -> anyhow::Result<()> { let migrations = get_migrations(); let num_migrations = migrations.len(); let tx = conn.transaction()?; @@ -198,7 +223,7 @@ fn app(conn: Connection) -> axum::Router { } #[tokio::main] -async fn main() -> Result<(), std::io::Error> { +async fn main() -> anyhow::Result<()> { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { @@ -239,10 +264,11 @@ async fn main() -> Result<(), std::io::Error> { .local_addr() .expect("failed to get local listening address") ); - axum::serve(listener, app).await + axum::serve(listener, app).await?; + Ok(()) } -fn get_foods(conn: &ConnState) -> rusqlite::Result> { +fn get_foods(conn: &ConnState) -> anyhow::Result> { let conn = conn.lock(); let mut stmt = PreparedStatements::get_foods(&conn); let foods: Vec<_> = stmt @@ -252,7 +278,7 @@ fn get_foods(conn: &ConnState) -> rusqlite::Result> { Ok(foods) } -fn get_sum(conn: &Arc>) -> rusqlite::Result { +fn get_sum(conn: &Arc>) -> anyhow::Result { let conn = conn.lock(); let mut stmt = PreparedStatements::get_sum(&conn); let sum = stmt.query_one((), |row| { @@ -272,22 +298,15 @@ fn get_date() -> String { date } -async fn root(State(conn): State) -> Result, StatusCode> { - let foods = get_foods(&conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let sum = get_sum(&conn).map_err(|e| { - error!(?e); - StatusCode::INTERNAL_SERVER_ERROR - })?; +async fn root(State(conn): State) -> Result, AppError> { + let foods = get_foods(&conn)?; + let sum = get_sum(&conn)?; let date = get_date(); let index = IndexTemplate { foods, sum, date }; - Ok(Html( - index - .render() - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, - )) + Ok(Html(index.render()?)) } -fn do_increase(conn: &Arc>, id: i32) -> rusqlite::Result<()> { +fn do_increase(conn: &Arc>, id: i32) -> anyhow::Result<()> { let conn = conn.lock(); let mut stmt = PreparedStatements::increase(&conn); let new: i32 = stmt.query_one((id,), |row| row.get(0))?; @@ -295,7 +314,7 @@ fn do_increase(conn: &Arc>, id: i32) -> rusqlite::Result<()> { Ok(()) } -fn get_food(conn: &Arc>, id: i32) -> rusqlite::Result { +fn get_food(conn: &Arc>, id: i32) -> anyhow::Result { let conn = conn.lock(); let mut stmt = PreparedStatements::get_food(&conn); let food = stmt.query_one((id,), Food::from_row)?; @@ -306,20 +325,16 @@ fn get_food(conn: &Arc>, id: i32) -> rusqlite::Result { async fn increase( State(conn): State>>, Path(id): Path, -) -> Result, StatusCode> { - do_increase(&conn, id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let food = get_food(&conn, id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let sum = get_sum(&conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; +) -> Result, AppError> { + do_increase(&conn, id)?; + let food = get_food(&conn, id)?; + let sum = get_sum(&conn)?; let date = get_date(); let update = FoodUpdateTemplate { food, sum, date }; - Ok(Html( - update - .render() - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, - )) + Ok(Html(update.render()?)) } -fn do_decrease(conn: &Arc>, id: i32) -> rusqlite::Result<()> { +fn do_decrease(conn: &Arc>, id: i32) -> anyhow::Result<()> { let conn = conn.lock(); let mut stmt = PreparedStatements::decrease(&conn); let new: i32 = stmt.query_one((id,), |row| row.get(0))?; @@ -330,20 +345,16 @@ fn do_decrease(conn: &Arc>, id: i32) -> rusqlite::Result<()> { async fn decrease( State(conn): State>>, Path(id): Path, -) -> Result, StatusCode> { - do_decrease(&conn, id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let food = get_food(&conn, id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let sum = get_sum(&conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; +) -> Result, AppError> { + do_decrease(&conn, id)?; + let food = get_food(&conn, id)?; + let sum = get_sum(&conn)?; let date = get_date(); let update = FoodUpdateTemplate { food, sum, date }; - Ok(Html( - update - .render() - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, - )) + Ok(Html(update.render()?)) } -fn do_set(conn: &Arc>, id: i32, amount: i32) -> rusqlite::Result<()> { +fn do_set(conn: &Arc>, id: i32, amount: i32) -> anyhow::Result<()> { let conn = conn.lock(); let mut stmt = PreparedStatements::set(&conn); let new: i32 = stmt.query_one((id, amount), |row| row.get(0))?; @@ -354,15 +365,11 @@ fn do_set(conn: &Arc>, id: i32, amount: i32) -> rusqlite::Resu async fn set( State(conn): State>>, Path((id, amount)): Path<(i32, i32)>, -) -> Result, StatusCode> { - do_set(&conn, id, amount).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let food = get_food(&conn, id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let sum = get_sum(&conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; +) -> Result, AppError> { + do_set(&conn, id, amount)?; + let food = get_food(&conn, id)?; + let sum = get_sum(&conn)?; let date = get_date(); let update = FoodUpdateTemplate { food, sum, date }; - Ok(Html( - update - .render() - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, - )) + Ok(Html(update.render()?)) }