Skip to main content

axum_error_sets/
lib.rs

1use axum::{
2    http::StatusCode,
3    response::{IntoResponse, Response},
4};
5use type_sets::Contains;
6
7pub type ApiResult<T, S> = Result<T, ApiResponse<S>>;
8
9pub trait WrapsResponse: Sized {
10    /// The status code associated with type.
11    const STATUS_CODE: StatusCode;
12
13    /// The inner value type that is wrapped by this status wrapper.
14    type Inner;
15
16    /// The pure type of this status wrapper, without any inner value.
17    type Pure: WrapsResponse<Inner = ()>;
18
19    /// Convert this status wrapper into its inner value.
20    fn into_inner(self) -> Self::Inner;
21
22    /// Convert this status wrapper into an [`ApiErrorSet`] with the
23    /// given inner value type. (`into` can be used as well)
24    fn into_set<E>(self) -> ApiResponse<E>
25    where
26        Self::Inner: IntoResponse,
27        E: Contains<Self>,
28    {
29        ApiResponse::new(self)
30    }
31}
32
33pub struct ApiResponse<S> {
34    response: Response,
35    code: StatusCode,
36    _marker: std::marker::PhantomData<fn() -> S>,
37}
38
39impl<S> ApiResponse<S> {
40    pub fn new<T>(wrapper: T) -> Self
41    where
42        S: Contains<T>,
43        T: WrapsResponse<Inner: IntoResponse>,
44    {
45        Self {
46            response: wrapper.into_inner().into_response(),
47            code: T::STATUS_CODE,
48            _marker: std::marker::PhantomData,
49        }
50    }
51}
52
53impl<S> IntoResponse for ApiResponse<S> {
54    fn into_response(self) -> Response {
55        (self.code, self.response).into_response()
56    }
57}
58
59macro_rules! impl_operation_output {
60    ($($n:tt => ($($E:ident),*)),+ $(,)?) => {
61        $(
62            #[cfg(feature = "aide")]
63            impl<$($E),*> aide::OperationOutput for ApiResponse<($($E,)*)>
64            where
65                $(
66                    $E: WrapsResponse<Inner: aide::OperationOutput>,
67                )*
68            {
69                type Inner = (StatusCode, Response);
70
71                fn operation_response(
72                    _ctx: &mut aide::generate::GenContext,
73                    _operation: &mut aide::openapi::Operation,
74                ) -> Option<aide::openapi::Response> {
75                    None
76                }
77
78                fn inferred_responses(
79                    ctx: &mut aide::generate::GenContext,
80                    operation: &mut aide::openapi::Operation,
81                ) -> Vec<(Option<u16>, aide::openapi::Response)> {
82                    vec![
83                        $((
84                            Some($E::STATUS_CODE.as_u16()),
85                            <$E::Inner as aide::OperationOutput>
86                                ::operation_response(ctx, operation)
87                                .unwrap_or_default(),
88                        )),*
89                    ]
90                }
91            }
92        )+
93    };
94}
95
96impl_operation_output!(
97    0 => (),
98    1 => (E1),
99    2 => (E1, E2),
100    3 => (E1, E2, E3),
101    4 => (E1, E2, E3, E4),
102    5 => (E1, E2, E3, E4, E5),
103    6 => (E1, E2, E3, E4, E5, E6),
104    7 => (E1, E2, E3, E4, E5, E6, E7),
105    8 => (E1, E2, E3, E4, E5, E6, E7, E8),
106);
107
108pub mod codes;