use axum::extract::{FromRequest, Request};
use axum_extra::extract::Form;
use serde::de::DeserializeOwned;
use validator::Validate;

use crate::errors::{AppError, bad_request};

/// Wrapper around [`axum_extra::extract::Form`] which returns an
/// [`AppError::BadRequest`] if [`validator`] checks on the target type do not
/// pass.
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct ValidatedForm<T: Validate>(pub(crate) T);

impl<T, S> FromRequest<S> for ValidatedForm<T>
where
    T: DeserializeOwned + Validate,
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        let Form(form) = Form::<T>::from_request(req, state)
            .await
            .map_err(|err| bad_request!("couldn't parse form: {err}"))?;
        form.validate()
            .map_err(|err| bad_request!("couldn't validate form: {err}"))?;
        Ok(ValidatedForm(form))
    }
}
