use std::ops::{Deref, DerefMut}; use axum::extract::{FromRequest, FromRequestParts, Request}; use axum::http::StatusCode; use axum::{async_trait, RequestPartsExt}; use axum::extract::rejection::FormRejection; use axum::response::{IntoResponse, IntoResponseParts}; use crate::app::routes::AppState; use crate::error::AppError; use super::htmx_form_data::{HtmxFormData, HtmxFormDataError}; #[derive(Debug, Clone, Copy, Default)] pub struct ValidatedForm(pub T); #[async_trait] impl FromRequest for ValidatedForm where T: HtmxFormData { type Rejection = ValidatedFormError; async fn from_request(req: Request, state: &AppState) -> Result { let raw_form_data = axum::Form::::from_request(req, state) .await .map_err(|e| ValidatedFormError::FormError(e))?; let value = raw_form_data.0 .validate(&state) .await .map_err(|e| match e { HtmxFormDataError::ValidationError(e) => ValidatedFormError::ValidationError(e), HtmxFormDataError::InternalServerError(e) => ValidatedFormError::InternalServerError(e), } )?; Ok(ValidatedForm(value)) } } impl Deref for ValidatedForm { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for ValidatedForm { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } pub enum ValidatedFormError { ValidationError(T), FormError(FormRejection), InternalServerError(anyhow::Error) } impl IntoResponse for ValidatedFormError where T: IntoResponse { fn into_response(self) -> axum::response::Response { match self { ValidatedFormError::FormError(err) => { err.into_response() }, ValidatedFormError::ValidationError(err) => { let (mut parts, body) = err.into_response().into_parts(); parts.status = StatusCode::OK; (parts, body).into_response() } ValidatedFormError::InternalServerError(err) => { AppError::from(err).into_response() } } } }