use axum::extract::rejection::{JsonRejection, QueryRejection};
use axum::extract::{FromRequest, FromRequestParts, Request};
use axum::http::request::Parts;
use crate::error::AppError;
pub struct Json<T>(pub T);
impl<S, T> FromRequest<S> for Json<T>
where
axum::Json<T>: FromRequest<S, Rejection = JsonRejection>,
S: Send + Sync,
{
type Rejection = AppError;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
match axum::Json::<T>::from_request(req, state).await {
Ok(axum::Json(valor)) => Ok(Self(valor)),
Err(rechazo) => Err(traducir(rechazo)),
}
}
}
fn traducir(rechazo: JsonRejection) -> AppError {
let motivo = match rechazo {
JsonRejection::MissingJsonContentType(_) => {
"la petición no declara un tipo de contenido JSON"
}
JsonRejection::JsonSyntaxError(_) => "el cuerpo no es JSON válido",
JsonRejection::JsonDataError(_) => "el cuerpo no tiene los campos que la ruta espera",
JsonRejection::BytesRejection(_) => "el cuerpo de la petición no se pudo leer",
_ => "el cuerpo de la petición no se pudo interpretar",
};
tracing::warn!(motivo, "no se pudo interpretar el cuerpo de la petición");
AppError::InvalidInput(format!(
"no se pudo interpretar el cuerpo de la petición: {motivo}"
))
}
pub struct Query<T>(pub T);
impl<S, T> FromRequestParts<S> for Query<T>
where
axum::extract::Query<T>: FromRequestParts<S, Rejection = QueryRejection>,
S: Send + Sync,
{
type Rejection = AppError;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
match axum::extract::Query::<T>::from_request_parts(parts, state).await {
Ok(axum::extract::Query(valor)) => Ok(Self(valor)),
Err(rechazo) => Err(traducir_consulta(rechazo)),
}
}
}
fn traducir_consulta(rechazo: QueryRejection) -> AppError {
let motivo = match rechazo {
QueryRejection::FailedToDeserializeQueryString(_) => {
"los parámetros de la consulta no tienen el tipo que la ruta espera"
}
_ => "los parámetros de la consulta no se pudieron interpretar",
};
tracing::warn!(
motivo,
"no se pudieron interpretar los parámetros de la consulta"
);
AppError::InvalidInput(format!(
"no se pudieron interpretar los parámetros de la consulta: {motivo}"
))
}