parent
93abc216e1
commit
c4b1d95cf7
@ -0,0 +1,3 @@
|
|||||||
|
pub fn form_checkbox_is_checked(val: &Option<String>) -> bool {
|
||||||
|
val.as_ref().map(|val| val == "on").unwrap_or(false)
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
use axum::async_trait;
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use crate::app::routes::AppState;
|
||||||
|
use crate::error::AppError;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait HtmxFormData : DeserializeOwned + Send {
|
||||||
|
type FormTemplate: IntoResponse;
|
||||||
|
|
||||||
|
async fn validate(self, state: &AppState) -> Result<Self, HtmxFormDataError<Self::FormTemplate>>;
|
||||||
|
|
||||||
|
async fn base_template(state: &AppState) -> anyhow::Result<Self::FormTemplate>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HtmxFormDataError<T> {
|
||||||
|
ValidationError(T),
|
||||||
|
InternalServerError(anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<anyhow::Error> for HtmxFormDataError<T> {
|
||||||
|
fn from(err: anyhow::Error) -> Self {
|
||||||
|
HtmxFormDataError::InternalServerError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
pub mod validated_form;
|
||||||
|
pub mod htmx_form_data;
|
||||||
|
pub mod form_helpers;
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
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<T>(pub T);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T> FromRequest<AppState> for ValidatedForm<T>
|
||||||
|
where
|
||||||
|
T: HtmxFormData
|
||||||
|
{
|
||||||
|
type Rejection = ValidatedFormError<T::FormTemplate>;
|
||||||
|
|
||||||
|
async fn from_request(req: Request, state: &AppState) -> Result<Self, Self::Rejection> {
|
||||||
|
|
||||||
|
let raw_form_data = axum::Form::<T>::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<T> Deref for ValidatedForm<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for ValidatedForm<T> {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ValidatedFormError<T> {
|
||||||
|
ValidationError(T),
|
||||||
|
FormError(FormRejection),
|
||||||
|
InternalServerError(anyhow::Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> IntoResponse for ValidatedFormError<T>
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue