use std::ops::{Deref, DerefMut};
use axum::{
Json,
extract::{FromRequest, FromRequestParts, Path as AxumPath, Request},
http::request::Parts,
};
use serde::de::DeserializeOwned;
use crate::{ApiError, options::JsonRejectionMode};
#[derive(Clone, Copy, Debug, Default)]
pub struct ApiJson<T>(
pub T,
);
impl<T, S> FromRequest<S> for ApiJson<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request(request: Request, state: &S) -> Result<Self, Self::Rejection> {
let rejection_mode = request
.extensions()
.get::<JsonRejectionMode>()
.cloned()
.unwrap_or_else(|| JsonRejectionMode::Legacy("validation_failed".to_owned()));
Json::<T>::from_request(request, state)
.await
.map(|Json(value)| Self(value))
.map_err(|rejection| ApiError::json_rejection(&rejection, &rejection_mode))
}
}
impl<T> Deref for ApiJson<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for ApiJson<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Path<T>(
pub T,
);
impl<T, S> FromRequestParts<S> for Path<T>
where
T: DeserializeOwned + Send,
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
AxumPath::<T>::from_request_parts(parts, state)
.await
.map(|AxumPath(value)| Self(value))
.map_err(ApiError::from)
}
}
impl<T> Deref for Path<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Path<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub use Path as ApiPath;
#[derive(Clone, Copy, Debug, Default)]
pub struct Query<T>(
pub T,
);
impl<T, S> FromRequestParts<S> for Query<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
serde_html_form::from_str(parts.uri.query().unwrap_or_default())
.map(Self)
.map_err(|_| ApiError::query_rejection())
}
}
impl<T> Deref for Query<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Query<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub use Query as ApiQuery;