use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use type_sets::{Contains, Superset};
pub type ApiResult<T, S> = Result<T, ApiResponse<S>>;
pub struct ApiResponse<S> {
response: Response,
code: StatusCode,
_marker: std::marker::PhantomData<fn() -> S>,
}
impl<S> ApiResponse<S> {
pub fn new<T>(wrapper: T) -> Self
where
S: Contains<T>,
T: StatusProvider<Inner: IntoResponse>,
{
Self::new_unchecked(wrapper.into_inner(), T::STATUS_CODE)
}
pub fn new_unchecked(response: impl IntoResponse, code: StatusCode) -> Self {
Self {
response: response.into_response(),
code,
_marker: std::marker::PhantomData,
}
}
pub fn into_parts(self) -> (Response, StatusCode) {
(self.response, self.code)
}
pub fn into_superset<U>(self) -> ApiResponse<U>
where
U: Superset<S>,
{
ApiResponse {
response: self.response,
code: self.code,
_marker: std::marker::PhantomData,
}
}
}
impl<S> IntoResponse for ApiResponse<S> {
fn into_response(self) -> Response {
(self.code, self.response).into_response()
}
}
impl<S> std::fmt::Debug for ApiResponse<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ApiResponse")
.field("response", &self.response)
.field("code", &self.code)
.finish()
}
}
macro_rules! utoipa_aide_impls {
($($n:tt => ($($E:ident),*)),+ $(,)?) => {
$(
#[allow(unused)]
#[cfg(feature = "aide")]
impl<$($E),*> aide::OperationOutput for ApiResponse<($($E,)*)>
where
$(
$E: StatusProvider<Inner: aide::OperationOutput>,
)*
{
type Inner = (StatusCode, Response);
fn operation_response(
_ctx: &mut aide::generate::GenContext,
_operation: &mut aide::openapi::Operation,
) -> Option<aide::openapi::Response> {
None
}
fn inferred_responses(
ctx: &mut aide::generate::GenContext,
operation: &mut aide::openapi::Operation,
) -> Vec<(Option<u16>, aide::openapi::Response)> {
vec![
$((
Some($E::STATUS_CODE.as_u16()),
<$E::Inner as aide::OperationOutput>
::operation_response(ctx, operation)
.unwrap_or_default(),
)),*
]
}
}
)+
};
}
utoipa_aide_impls!(
0 => (),
1 => (E1),
2 => (E1, E2),
3 => (E1, E2, E3),
4 => (E1, E2, E3, E4),
5 => (E1, E2, E3, E4, E5),
6 => (E1, E2, E3, E4, E5, E6),
7 => (E1, E2, E3, E4, E5, E6, E7),
8 => (E1, E2, E3, E4, E5, E6, E7, E8),
9 => (E1, E2, E3, E4, E5, E6, E7, E8, E9),
10 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10),
11 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11),
12 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12),
13 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13),
14 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14),
15 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15),
16 => (E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15, E16),
);
pub trait StatusProvider: From<Self::Inner> + Sized {
const STATUS_CODE: StatusCode;
type Inner;
type WithInner<T>: StatusProvider<Inner = T>;
fn into_inner(self) -> Self::Inner;
fn into_set<E>(self) -> ApiResponse<E>
where
Self::Inner: IntoResponse,
E: Contains<Self>,
{
ApiResponse::new(self)
}
fn map<T>(self, f: impl FnOnce(Self::Inner) -> T) -> Self::WithInner<T> {
<Self::WithInner<T> as From<T>>::from(f(self.into_inner()))
}
}
pub trait ResultStatusExt<T, E>: Sized {
fn with_status<S>(self) -> Result<T, S::WithInner<E>>
where
S: StatusProvider<Inner = ()>;
fn into_status<S, E2>(self) -> Result<T, S::WithInner<E2>>
where
S: StatusProvider<Inner = ()>,
E: Into<E2>;
fn change_status<S>(self) -> Result<T, S::WithInner<E::Inner>>
where
E: StatusProvider,
S: StatusProvider;
fn map_status<F, O>(self, f: F) -> Result<T, E::WithInner<O>>
where
E: StatusProvider,
F: FnOnce(E::Inner) -> O;
fn map_status_into<O>(self) -> Result<T, E::WithInner<O>>
where
E: StatusProvider,
E::Inner: Into<O>,
{
self.map_status(Into::into)
}
}
impl<T, E> ResultStatusExt<T, E> for Result<T, E> {
fn into_status<S, E2>(self) -> Result<T, S::WithInner<E2>>
where
S: StatusProvider<Inner = ()>,
E: Into<E2>,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(S::WithInner::from(e.into())),
}
}
fn with_status<S>(self) -> Result<T, S::WithInner<E>>
where
S: StatusProvider<Inner = ()>,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(S::WithInner::from(e)),
}
}
fn map_status<F, O>(self, f: F) -> Result<T, E::WithInner<O>>
where
E: StatusProvider,
F: FnOnce(E::Inner) -> O,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(e.map(f)),
}
}
fn change_status<S>(self) -> Result<T, S::WithInner<E::Inner>>
where
E: StatusProvider,
S: StatusProvider,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(S::WithInner::from(e.into_inner())),
}
}
}
pub trait ApiResultExt<T, S> {
fn into_superset<U>(self) -> Result<T, ApiResponse<U>>
where
U: Superset<S>;
}
impl<T, S> ApiResultExt<T, S> for Result<T, ApiResponse<S>> {
fn into_superset<U>(self) -> Result<T, ApiResponse<U>>
where
U: Superset<S>,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(e.into_superset()),
}
}
}
pub mod codes;