From 09c50411d16db32565db0949c4fdcffc4ff604d1 Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Tue, 21 Nov 2023 15:30:30 +0100 Subject: [PATCH] Switch from "impl Responder" to Result<> response --- src/db/errors.rs | 2 +- src/handlers/errors.rs | 55 ++++++++--- src/handlers/v1/schedules.rs | 175 +++++++++++------------------------ 3 files changed, 99 insertions(+), 133 deletions(-) diff --git a/src/db/errors.rs b/src/db/errors.rs index 37c3d4d..f71d109 100644 --- a/src/db/errors.rs +++ b/src/db/errors.rs @@ -16,7 +16,7 @@ pub enum DatabaseError { } impl DatabaseError { - fn get_code(&self) -> StatusCode { + pub fn get_code(&self) -> StatusCode { match self { DatabaseError::NotFound => StatusCode::NOT_FOUND, DatabaseError::Protected => StatusCode::FORBIDDEN, diff --git a/src/handlers/errors.rs b/src/handlers/errors.rs index 47bef11..6cc8cf1 100644 --- a/src/handlers/errors.rs +++ b/src/handlers/errors.rs @@ -1,24 +1,28 @@ +use crate::db::errors::DatabaseError; use actix_web::http::StatusCode; use actix_web::HttpResponse; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; +use std::fmt::{Display, Formatter}; #[derive(Debug)] -pub enum HandlerError { +pub enum ApiError { BadUid, ProtectedSchedule, + DatabaseError(DatabaseError), } -impl HandlerError { +impl ApiError { fn get_code(&self) -> StatusCode { match self { - HandlerError::BadUid => StatusCode::BAD_REQUEST, - HandlerError::ProtectedSchedule => StatusCode::FORBIDDEN, + ApiError::BadUid => StatusCode::BAD_REQUEST, + ApiError::ProtectedSchedule => StatusCode::FORBIDDEN, + ApiError::DatabaseError(db_error) => db_error.get_code(), } } } -impl Serialize for HandlerError { +impl Serialize for ApiError { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -30,17 +34,46 @@ impl Serialize for HandlerError { } } -impl From<&HandlerError> for String { - fn from(err: &HandlerError) -> Self { +impl From<&ApiError> for String { + fn from(err: &ApiError) -> Self { match err { - HandlerError::BadUid => String::from("the uid is in a bad format"), - HandlerError::ProtectedSchedule => String::from("the targeted schedule is protected"), + ApiError::BadUid => String::from("the uid is in a bad format"), + ApiError::ProtectedSchedule => String::from("the targeted schedule is protected"), + ApiError::DatabaseError(db_err) => String::from(db_err), } } } -impl From for HttpResponse { - fn from(err: HandlerError) -> Self { +impl From<&ApiError> for HttpResponse { + fn from(err: &ApiError) -> Self { HttpResponse::build(err.get_code()).json(err) } } + +impl Display for ApiError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.get_code(), String::from(self)) + } +} + +impl actix_web::error::ResponseError for ApiError { + fn status_code(&self) -> StatusCode { + self.get_code() + } + + fn error_response(&self) -> HttpResponse { + HttpResponse::from(self) + } +} + +impl From for ApiError { + fn from(err: sqlx::Error) -> Self { + ApiError::DatabaseError(DatabaseError::from(err)) + } +} + +impl From for ApiError { + fn from(err: DatabaseError) -> Self { + ApiError::DatabaseError(err) + } +} diff --git a/src/handlers/v1/schedules.rs b/src/handlers/v1/schedules.rs index 594ec7e..95237ef 100644 --- a/src/handlers/v1/schedules.rs +++ b/src/handlers/v1/schedules.rs @@ -1,7 +1,7 @@ use std::borrow::Borrow; use std::convert::TryFrom; -use actix_web::{delete, get, post, put, web, HttpResponse, Responder}; +use actix_web::{delete, get, post, put, web, HttpResponse}; use serde::{Deserialize, Serialize}; use sqlx::pool::PoolConnection; use sqlx::{Pool, Sqlite}; @@ -10,7 +10,7 @@ use crate::db::errors::DatabaseError; use crate::db::models::{Periods, Schedule}; use crate::db::schedules::*; use crate::db::tag::get_tag; -use crate::handlers::errors::HandlerError; +use crate::handlers::errors::ApiError; use crate::return_models::ReturnSchedule; use crate::types::EmgauwaUid; use crate::utils::vec_has_error; @@ -23,19 +23,10 @@ pub struct RequestSchedule { } #[get("/api/v1/schedules")] -pub async fn index(pool: web::Data>) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +pub async fn index(pool: web::Data>) -> Result { + let mut pool_conn = pool.acquire().await?; - let schedules = get_schedules(&mut pool_conn).await; - - if let Err(err) = schedules { - return HttpResponse::from(err); - } - let schedules = schedules.unwrap(); + let schedules = get_schedules(&mut pool_conn).await?; let mut return_schedules: Vec = schedules.iter().map(ReturnSchedule::from).collect(); @@ -43,91 +34,60 @@ pub async fn index(pool: web::Data>) -> impl Responder { schedule.load_tags(&mut pool_conn); } - HttpResponse::Ok().json(return_schedules) + Ok(HttpResponse::Ok().json(return_schedules)) } #[get("/api/v1/schedules/tag/{tag}")] -pub async fn tagged(pool: web::Data>, path: web::Path<(String,)>) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +pub async fn tagged( + pool: web::Data>, + path: web::Path<(String,)>, +) -> Result { + let mut pool_conn = pool.acquire().await?; let (tag,) = path.into_inner(); - let tag_db = get_tag(&mut pool_conn, &tag).await; - if let Err(err) = tag_db { - return HttpResponse::from(err); - } - let tag_db = tag_db.unwrap(); + let tag_db = get_tag(&mut pool_conn, &tag).await?; - let schedules = get_schedules_by_tag(&mut pool_conn, &tag_db).await; - if let Err(err) = schedules { - return HttpResponse::from(err); - } - let schedules = schedules.unwrap(); + let schedules = get_schedules_by_tag(&mut pool_conn, &tag_db).await?; let mut return_schedules: Vec = schedules.iter().map(ReturnSchedule::from).collect(); for schedule in return_schedules.iter_mut() { schedule.load_tags(&mut pool_conn); } - HttpResponse::Ok().json(return_schedules) + Ok(HttpResponse::Ok().json(return_schedules)) } #[get("/api/v1/schedules/{schedule_id}")] -pub async fn show(pool: web::Data>, path: web::Path<(String,)>) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +pub async fn show( + pool: web::Data>, + path: web::Path<(String,)>, +) -> Result { + let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid)); + let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - match emgauwa_uid { - Ok(uid) => { - let schedule = get_schedule_by_uid(&mut pool_conn, &uid).await; - match schedule { - Ok(ok) => { - let mut return_schedule = ReturnSchedule::from(ok); - return_schedule.load_tags(&mut pool_conn); - HttpResponse::Ok().json(return_schedule) - } - Err(err) => HttpResponse::from(err), - } - } - Err(err) => HttpResponse::from(err), - } + let schedule = get_schedule_by_uid(&mut pool_conn, &emgauwa_uid).await?; + + let mut return_schedule = ReturnSchedule::from(schedule); + return_schedule.load_tags(&mut pool_conn); + Ok(HttpResponse::Ok().json(return_schedule)) } #[post("/api/v1/schedules")] pub async fn add( pool: web::Data>, data: web::Json, -) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +) -> Result { + let mut pool_conn = pool.acquire().await?; - let new_schedule = create_schedule(&mut pool_conn, &data.name, &data.periods).await; + let new_schedule = create_schedule(&mut pool_conn, &data.name, &data.periods).await?; - if let Err(err) = new_schedule { - return HttpResponse::from(err); - } - let new_schedule = new_schedule.unwrap(); - - let result = set_schedule_tags(&mut pool_conn, &new_schedule, data.tags.as_slice()).await; - if let Err(err) = result { - return HttpResponse::from(err); - } + set_schedule_tags(&mut pool_conn, &new_schedule, data.tags.as_slice()).await?; let mut return_schedule = ReturnSchedule::from(new_schedule); return_schedule.load_tags(&mut pool_conn); - HttpResponse::Created().json(return_schedule) + Ok(HttpResponse::Created().json(return_schedule)) } async fn add_list_single( @@ -146,12 +106,8 @@ async fn add_list_single( pub async fn add_list( pool: web::Data>, data: web::Json>, -) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +) -> Result { + let mut pool_conn = pool.acquire().await?; let result: Vec> = data .as_slice() @@ -162,13 +118,13 @@ pub async fn add_list( .collect(); match vec_has_error(&result) { - true => HttpResponse::from( + true => Ok(HttpResponse::from( result .into_iter() .find(|r| r.is_err()) .unwrap() .unwrap_err(), - ), + )), false => { let mut return_schedules: Vec = result .iter() @@ -178,7 +134,7 @@ pub async fn add_list( for schedule in return_schedules.iter_mut() { schedule.load_tags(&mut pool_conn); } - HttpResponse::Created().json(return_schedules) + Ok(HttpResponse::Created().json(return_schedules)) } } } @@ -188,25 +144,13 @@ pub async fn update( pool: web::Data>, path: web::Path<(String,)>, data: web::Json, -) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +) -> Result { + let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid)); - if let Err(err) = emgauwa_uid { - return HttpResponse::from(err); - } - let emgauwa_uid = emgauwa_uid.unwrap(); + let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; - let schedule = get_schedule_by_uid(&mut pool_conn, &emgauwa_uid).await; - if let Err(err) = schedule { - return HttpResponse::from(err); - } - let schedule = schedule.unwrap(); + let schedule = get_schedule_by_uid(&mut pool_conn, &emgauwa_uid).await?; let schedule = update_schedule( &mut pool_conn, @@ -214,42 +158,31 @@ pub async fn update( data.name.as_str(), data.periods.borrow(), ) - .await; - if let Err(err) = schedule { - return HttpResponse::from(err); - } - let schedule = schedule.unwrap(); + .await?; - let result = set_schedule_tags(&mut pool_conn, &schedule, data.tags.as_slice()).await; - if let Err(err) = result { - return HttpResponse::from(err); - } + set_schedule_tags(&mut pool_conn, &schedule, data.tags.as_slice()).await?; let mut return_schedule = ReturnSchedule::from(schedule); return_schedule.load_tags(&mut pool_conn); - HttpResponse::Ok().json(return_schedule) + Ok(HttpResponse::Ok().json(return_schedule)) } #[delete("/api/v1/schedules/{schedule_id}")] -pub async fn delete(pool: web::Data>, path: web::Path<(String,)>) -> impl Responder { - let pool_conn = pool.acquire().await; - if let Err(err) = pool_conn { - return HttpResponse::from(DatabaseError::from(err)); - } - let mut pool_conn = pool_conn.unwrap(); +pub async fn delete( + pool: web::Data>, + path: web::Path<(String,)>, +) -> Result { + let mut pool_conn = pool.acquire().await?; let (schedule_uid,) = path.into_inner(); - let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid)); + let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(ApiError::BadUid))?; match emgauwa_uid { - Ok(uid) => match uid { - EmgauwaUid::Off => HttpResponse::from(HandlerError::ProtectedSchedule), - EmgauwaUid::On => HttpResponse::from(HandlerError::ProtectedSchedule), - EmgauwaUid::Any(_) => match delete_schedule_by_uid(&mut pool_conn, uid).await { - Ok(_) => HttpResponse::Ok().json("schedule got deleted"), - Err(err) => HttpResponse::from(err), - }, - }, - Err(err) => HttpResponse::from(err), + EmgauwaUid::Off => Err(ApiError::ProtectedSchedule), + EmgauwaUid::On => Err(ApiError::ProtectedSchedule), + EmgauwaUid::Any(_) => { + delete_schedule_by_uid(&mut pool_conn, emgauwa_uid).await?; + Ok(HttpResponse::Ok().json("schedule got deleted")) + } } }