axum_extra/extract/
optional_path.rs1use axum::{
2 extract::{rejection::PathRejection, FromRequestParts, Path},
3 RequestPartsExt,
4};
5use serde::de::DeserializeOwned;
6
7#[deprecated = "Use Option<Path<_>> instead"]
35#[derive(Debug)]
36pub struct OptionalPath<T>(pub Option<T>);
37
38#[allow(deprecated)]
39impl<T, S> FromRequestParts<S> for OptionalPath<T>
40where
41 T: DeserializeOwned + Send + 'static,
42 S: Send + Sync,
43{
44 type Rejection = PathRejection;
45
46 async fn from_request_parts(
47 parts: &mut http::request::Parts,
48 _: &S,
49 ) -> Result<Self, Self::Rejection> {
50 parts
51 .extract::<Option<Path<T>>>()
52 .await
53 .map(|opt| Self(opt.map(|Path(x)| x)))
54 }
55}
56
57#[cfg(test)]
58#[allow(deprecated)]
59mod tests {
60 use std::num::NonZeroU32;
61
62 use axum::{routing::get, Router};
63
64 use super::OptionalPath;
65 use crate::test_helpers::TestClient;
66
67 #[crate::test]
68 async fn supports_128_bit_numbers() {
69 async fn handle(OptionalPath(param): OptionalPath<NonZeroU32>) -> String {
70 let num = param.map_or(0, |p| p.get());
71 format!("Success: {num}")
72 }
73
74 let app = Router::new()
75 .route("/", get(handle))
76 .route("/{num}", get(handle));
77
78 let client = TestClient::new(app);
79
80 let res = client.get("/").await;
81 assert_eq!(res.text().await, "Success: 0");
82
83 let res = client.get("/1").await;
84 assert_eq!(res.text().await, "Success: 1");
85
86 let res = client.get("/0").await;
87 assert_eq!(
88 res.text().await,
89 "Invalid URL: invalid value: integer `0`, expected a nonzero u32"
90 );
91
92 let res = client.get("/NaN").await;
93 assert_eq!(
94 res.text().await,
95 "Invalid URL: Cannot parse `NaN` to a `u32`"
96 );
97 }
98}