1#![doc = include_str!("../README.md")]
2
3use axum::{
4 extract::{rejection::PathRejection, FromRequest, FromRequestParts, Path, Query},
5 http::Request,
6};
7use serde::Deserialize;
8
9pub struct ValidOption<T>(pub Option<T>);
16
17#[axum::async_trait]
18impl<S, T> FromRequestParts<S> for ValidOption<T>
19where
20 S: Send + Sync,
21 T: FromRequestPartsOptional<S> + Send + Sync,
22 T::Rejection: Send + Sync,
23{
24 type Rejection = <T as FromRequestParts<S>>::Rejection;
25
26 async fn from_request_parts(
27 parts: &mut axum::http::request::Parts,
28 state: &S,
29 ) -> Result<Self, Self::Rejection> {
30 T::option_reject(T::from_request_parts(parts, state).await)
31 .await
32 .map(ValidOption)
33 }
34}
35
36#[axum::async_trait]
37impl<S, B, T> FromRequest<S, B> for ValidOption<T>
38where
39 S: Send + Sync,
40 B: Sync + Send + 'static,
41 T: FromRequestOptional<S, B> + Send + Sync,
42 T::Rejection: Send + Sync,
43{
44 type Rejection = <T as FromRequest<S, B>>::Rejection;
45
46 async fn from_request(req: Request<B>, body: &S) -> Result<Self, Self::Rejection> {
47 T::option_reject(T::from_request(req, body).await)
48 .await
49 .map(ValidOption)
50 }
51}
52
53#[axum::async_trait]
54#[cfg_attr(
55 nightly_error_messages,
56 rustc_on_unimplemented(
57 note = "Function argument is not a valid optional extractor. \nSee `https://docs.rs/axum-option/latest/axum-option` for details",
58 )
59)]
60pub trait FromRequestPartsOptional<S>: FromRequestParts<S> {
61 async fn option_reject(
62 result: Result<Self, Self::Rejection>,
63 ) -> Result<Option<Self>, Self::Rejection>;
64}
65
66#[axum::async_trait]
67#[cfg_attr(
68 nightly_error_messages,
69 rustc_on_unimplemented(
70 note = "Function argument is not a valid optional extractor. \nSee `https://docs.rs/axum-option/latest/axum-option` for details",
71 )
72)]
73pub trait FromRequestOptional<S, B>: FromRequest<S, B> {
74 async fn option_reject(
75 result: Result<Self, Self::Rejection>,
76 ) -> Result<Option<Self>, Self::Rejection>;
77}
78
79#[axum::async_trait]
82impl<S: Send + Sync, T> FromRequestPartsOptional<S> for Query<T>
83where
84 T: std::fmt::Debug + for<'de> Deserialize<'de> + Send + Sync,
85{
86 async fn option_reject(
87 result: Result<Self, Self::Rejection>,
88 ) -> Result<Option<Self>, Self::Rejection> {
89 println!("{:?}", result);
90 match result {
91 Ok(query) => Ok(Some(query)),
92 Err(e) => Err(e),
94 }
95 }
96}
97
98#[axum::async_trait]
101impl<S: Send + Sync, T: Send + Sync + for<'de> Deserialize<'de>> FromRequestPartsOptional<S>
102 for Path<T>
103{
104 async fn option_reject(
105 result: Result<Self, Self::Rejection>,
106 ) -> Result<Option<Self>, Self::Rejection> {
107 match result {
108 Ok(p) => Ok(Some(p)),
109 Err(PathRejection::MissingPathParams(_)) => Ok(None),
110 Err(e) => Err(e),
111 }
112 }
113}
114
115#[cfg(feature = "headers")]
116mod headers {
117 use axum::extract::rejection::TypedHeaderRejectionReason;
118 use axum::headers::Header;
119 use axum::TypedHeader;
120
121 use super::FromRequestPartsOptional;
122
123 #[axum::async_trait]
124 impl<S: Send + Sync, T: Header + Send + Sync> FromRequestPartsOptional<S> for TypedHeader<T> {
125 async fn option_reject(
126 result: Result<Self, Self::Rejection>,
127 ) -> Result<Option<Self>, Self::Rejection> {
128 match result {
129 Ok(header) => Ok(Some(header)),
130 Err(e) if matches!(e.reason(), TypedHeaderRejectionReason::Missing) => Ok(None),
131 Err(e) => Err(e),
132 }
133 }
134 }
135}
136
137#[cfg(feature = "jwt-authorizer")]
138mod jwt_authorizer {
139 use jwt_authorizer::{error::AuthError, JwtClaims};
140 use serde::Deserialize;
141
142 use super::FromRequestPartsOptional;
143
144 #[axum::async_trait]
145 impl<S, T> FromRequestPartsOptional<S> for JwtClaims<T>
146 where
147 S: Send + Sync,
148 T: Clone + 'static + Sync + Send + for<'de> Deserialize<'de>,
149 {
150 async fn option_reject(
151 result: Result<Self, Self::Rejection>,
152 ) -> Result<Option<Self>, Self::Rejection> {
153 match result {
154 Ok(claims) => Ok(Some(claims)),
155 Err(AuthError::MissingToken()) => Ok(None),
156 Err(e) => Err(e),
157 }
158 }
159 }
160}