refactor: custom context for validation

This commit is contained in:
Max Hohlfeld 2025-06-22 21:33:01 +02:00
parent 9666932915
commit b2969b988d
5 changed files with 28 additions and 12 deletions

View File

@ -4,7 +4,7 @@ use sqlx::PgPool;
use crate::{ use crate::{
endpoints::{user::NewOrEditUserForm, IdPath}, endpoints::{user::NewOrEditUserForm, IdPath},
models::{Function, Role, User, UserChangeset}, models::{Function, Role, User, UserChangeset},
utils::{validation::AsyncValidate, ApplicationError}, utils::{validation::{AsyncValidate, DbContext}, ApplicationError},
}; };
#[actix_web::post("/users/edit/{id}")] #[actix_web::post("/users/edit/{id}")]
@ -63,7 +63,8 @@ pub async fn post_edit(
} }
} }
if let Err(e) = changeset.validate_with_pool(pool.get_ref()).await { let context = DbContext::new(pool.get_ref());
if let Err(e) = changeset.validate_with_context(&context).await {
return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); return Ok(HttpResponse::UnprocessableEntity().body(e.to_string()));
}; };

View File

@ -5,7 +5,7 @@ use crate::{
endpoints::user::NewOrEditUserForm, endpoints::user::NewOrEditUserForm,
mail::Mailer, mail::Mailer,
models::{Function, Registration, Role, User, UserChangeset}, models::{Function, Registration, Role, User, UserChangeset},
utils::{validation::AsyncValidate, ApplicationError}, utils::{validation::{AsyncValidate, DbContext}, ApplicationError},
}; };
#[actix_web::post("/users/new")] #[actix_web::post("/users/new")]
@ -57,7 +57,8 @@ pub async fn post_new(
.body("email: an user already exists with the same email")); .body("email: an user already exists with the same email"));
} }
if let Err(e) = changeset.validate_with_pool(pool.get_ref()).await { let context = DbContext::new(pool.get_ref());
if let Err(e) = changeset.validate_with_context(&context).await {
return Ok(HttpResponse::UnprocessableEntity().body(e.to_string())); return Ok(HttpResponse::UnprocessableEntity().body(e.to_string()));
}; };

View File

@ -3,7 +3,7 @@ use fake::{faker::internet::en::SafeEmail, faker::name::en::Name, Dummy};
use sqlx::PgPool; use sqlx::PgPool;
use crate::utils::validation::{email_is_valid, AsyncValidate, AsyncValidateError}; use crate::utils::validation::{email_is_valid, AsyncValidate, AsyncValidateError, DbContext};
use super::{Area, Function, Role}; use super::{Area, Function, Role};
@ -22,13 +22,15 @@ pub struct UserChangeset {
pub area_id: i32, pub area_id: i32,
} }
impl AsyncValidate for UserChangeset { impl <'a>AsyncValidate<'a> for UserChangeset {
async fn validate_with_pool(&self, pool: &sqlx::PgPool) -> Result<(), AsyncValidateError> { async fn validate_with_context(&self, context: &'a Self::Context) -> Result<(), AsyncValidateError> {
email_is_valid(&self.email)?; email_is_valid(&self.email)?;
area_exists(pool, self.area_id).await?; area_exists(context.pool, self.area_id).await?;
Ok(()) Ok(())
} }
type Context = DbContext<'a>;
} }
async fn area_exists(pool: &PgPool, id: i32) -> Result<(), AsyncValidateError> { async fn area_exists(pool: &PgPool, id: i32) -> Result<(), AsyncValidateError> {

View File

@ -5,3 +5,14 @@ mod r#trait;
pub use email::email_is_valid; pub use email::email_is_valid;
pub use error::AsyncValidateError; pub use error::AsyncValidateError;
pub use r#trait::AsyncValidate; pub use r#trait::AsyncValidate;
use sqlx::PgPool;
pub struct DbContext<'a> {
pub pool: &'a PgPool,
}
impl<'a> DbContext<'a> {
pub fn new(pool: &'a PgPool) -> Self {
Self { pool }
}
}

View File

@ -1,7 +1,8 @@
use sqlx::PgPool;
use super::AsyncValidateError; use super::AsyncValidateError;
pub trait AsyncValidate { pub trait AsyncValidate<'a> {
async fn validate_with_pool(&self, pool: &PgPool) -> Result<(), AsyncValidateError>; type Context: 'a;
async fn validate_with_context(&self, context: &'a Self::Context) -> Result<(), AsyncValidateError>;
} }