use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use type_sets::Contains;
pub type ApiResult<T, S> = Result<T, ApiResponse<S>>;
pub trait WrapsResponse: Sized {
const STATUS_CODE: StatusCode;
type Inner;
type Pure: WrapsResponse<Inner = ()>;
fn into_inner(self) -> Self::Inner;
fn into_set<E>(self) -> ApiResponse<E>
where
Self::Inner: IntoResponse,
E: Contains<Self>,
{
ApiResponse::new(self)
}
}
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: WrapsResponse<Inner: IntoResponse>,
{
Self {
response: wrapper.into_inner().into_response(),
code: T::STATUS_CODE,
_marker: std::marker::PhantomData,
}
}
}
impl<S> IntoResponse for ApiResponse<S> {
fn into_response(self) -> Response {
(self.code, self.response).into_response()
}
}
macro_rules! impl_operation_output {
($($n:tt => ($($E:ident),*)),+ $(,)?) => {
$(
#[cfg(feature = "aide")]
impl<$($E),*> aide::OperationOutput for ApiResponse<($($E,)*)>
where
$(
$E: WrapsResponse<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(),
)),*
]
}
}
)+
};
}
impl_operation_output!(
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),
);
pub mod codes;