From ede4d8545ece482aa9894777860b889bdc1c0774 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 10:39:05 +0200 Subject: [PATCH 01/19] fix: add `#[must_use]` to relevant functions Add `#[must_use]` attribute to functions where the attribute could be added. This fixes the `must_use_candidate` clippy lint. --- Cargo.toml | 1 + axum-core/src/body.rs | 2 ++ axum-core/src/error.rs | 1 + axum-core/src/macros.rs | 4 ++++ axum-extra/src/extract/cookie/mod.rs | 3 +++ axum-extra/src/extract/cookie/private.rs | 4 ++++ axum-extra/src/extract/cookie/signed.rs | 4 ++++ axum-extra/src/extract/multipart.rs | 5 +++++ axum-extra/src/response/multiple.rs | 3 +++ axum-extra/src/routing/mod.rs | 1 + axum-extra/src/typed_header.rs | 2 ++ axum/src/extract/matched_path.rs | 1 + axum/src/extract/multipart.rs | 6 ++++++ axum/src/extract/nested_path.rs | 1 + axum/src/extract/path/mod.rs | 7 +++++++ axum/src/extract/ws.rs | 1 + axum/src/response/redirect.rs | 2 ++ axum/src/routing/method_filter.rs | 1 + axum/src/routing/method_routing.rs | 2 ++ axum/src/routing/mod.rs | 4 ++++ axum/src/test_helpers/test_client.rs | 6 ++++++ 21 files changed, 61 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a9eeafcfd3..ce5f15c826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ uninlined_format_args = "warn" unnested_or_patterns = "warn" unused_self = "warn" verbose_file_reads = "warn" +must_use_candidate = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 6c49970b96..56e9829022 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -48,6 +48,7 @@ impl Body { } /// Create an empty body. + #[must_use] pub fn empty() -> Self { Self::new(http_body_util::Empty::new()) } @@ -72,6 +73,7 @@ impl Body { /// you need a [`Stream`] of all frame types. /// /// [`http_body_util::BodyStream`]: https://docs.rs/http-body-util/latest/http_body_util/struct.BodyStream.html + #[must_use] pub fn into_data_stream(self) -> BodyDataStream { BodyDataStream { inner: self } } diff --git a/axum-core/src/error.rs b/axum-core/src/error.rs index 8c522c72b2..e77340e3cf 100644 --- a/axum-core/src/error.rs +++ b/axum-core/src/error.rs @@ -16,6 +16,7 @@ impl Error { } /// Convert an `Error` back into the underlying boxed trait object. + #[must_use] pub fn into_inner(self) -> BoxError { self.inner } diff --git a/axum-core/src/macros.rs b/axum-core/src/macros.rs index 8f2762486e..6bc24c3027 100644 --- a/axum-core/src/macros.rs +++ b/axum-core/src/macros.rs @@ -106,11 +106,13 @@ macro_rules! __define_rejection { } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { http::StatusCode::$status } @@ -179,6 +181,7 @@ macro_rules! __composite_rejection { impl $name { /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { match self { $( @@ -188,6 +191,7 @@ macro_rules! __composite_rejection { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { match self { $( diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 50fa6031ac..6b91bdb088 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -118,6 +118,7 @@ impl CookieJar { /// run extractors. Normally you should create `CookieJar`s through [`FromRequestParts`]. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn from_headers(headers: &HeaderMap) -> Self { let mut jar = cookie::CookieJar::new(); for cookie in cookies_from_request(headers) { @@ -135,6 +136,7 @@ impl CookieJar { /// CookieJar`. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn new() -> Self { Self::default() } @@ -153,6 +155,7 @@ impl CookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option<&Cookie<'static>> { self.jar.get(name) } diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index f852b8c4ba..48ba753f6a 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -153,6 +153,7 @@ impl PrivateCookieJar { /// run extractors. Normally you should create `PrivateCookieJar`s through [`FromRequestParts`]. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn from_headers(headers: &HeaderMap, key: Key) -> Self { let mut jar = cookie::CookieJar::new(); let mut private_jar = jar.private_mut(&key); @@ -175,6 +176,7 @@ impl PrivateCookieJar { /// run extractors. Normally you should create `PrivateCookieJar`s through [`FromRequestParts`]. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn new(key: Key) -> Self { Self { jar: Default::default(), @@ -201,6 +203,7 @@ impl PrivateCookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option> { self.private_jar().get(name) } @@ -246,6 +249,7 @@ impl PrivateCookieJar { /// Authenticates and decrypts `cookie`, returning the plaintext version if decryption succeeds /// or `None` otherwise. + #[must_use] pub fn decrypt(&self, cookie: Cookie<'static>) -> Option> { self.private_jar().decrypt(cookie) } diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 92bf917145..662638c8c3 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -170,6 +170,7 @@ impl SignedCookieJar { /// run extractors. Normally you should create `SignedCookieJar`s through [`FromRequestParts`]. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn from_headers(headers: &HeaderMap, key: Key) -> Self { let mut jar = cookie::CookieJar::new(); let mut signed_jar = jar.signed_mut(&key); @@ -192,6 +193,7 @@ impl SignedCookieJar { /// run extractors. Normally you should create `SignedCookieJar`s through [`FromRequestParts`]. /// /// [`FromRequestParts`]: axum::extract::FromRequestParts + #[must_use] pub fn new(key: Key) -> Self { Self { jar: Default::default(), @@ -219,6 +221,7 @@ impl SignedCookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option> { self.signed_jar().get(name) } @@ -264,6 +267,7 @@ impl SignedCookieJar { /// Verifies the authenticity and integrity of `cookie`, returning the plaintext version if /// verification succeeds or `None` otherwise. + #[must_use] pub fn verify(&self, cookie: Cookie<'static>) -> Option> { self.signed_jar().verify(cookie) } diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index e92dc1788e..39d67a0c92 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -150,6 +150,7 @@ impl Field { /// The field name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() } @@ -157,16 +158,19 @@ impl Field { /// The file name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn file_name(&self) -> Option<&str> { self.inner.file_name() } /// Get the [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of the field. + #[must_use] pub fn content_type(&self) -> Option<&str> { self.inner.content_type().map(|m| m.as_ref()) } /// Get a map of headers as [`HeaderMap`]. + #[must_use] pub fn headers(&self) -> &HeaderMap { self.inner.headers() } @@ -253,6 +257,7 @@ impl MultipartError { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { status_code_from_multer_error(&self.source) } diff --git a/axum-extra/src/response/multiple.rs b/axum-extra/src/response/multiple.rs index 390ef3e726..ce3fdd6b46 100644 --- a/axum-extra/src/response/multiple.rs +++ b/axum-extra/src/response/multiple.rs @@ -24,6 +24,7 @@ impl MultipartForm { /// let parts: Vec = vec![Part::text("foo".to_string(), "abc"), Part::text("bar".to_string(), "def")]; /// let form = MultipartForm::with_parts(parts); /// ``` + #[must_use] pub fn with_parts(parts: Vec) -> Self { MultipartForm { parts } } @@ -103,6 +104,7 @@ impl Part { /// let parts: Vec = vec![Part::text("foo".to_string(), "abc")]; /// let form = MultipartForm::from_iter(parts); /// ``` + #[must_use] pub fn text(name: String, contents: &str) -> Self { Self { name, @@ -127,6 +129,7 @@ impl Part { /// let parts: Vec = vec![Part::file("foo", "foo.txt", vec![0x68, 0x68, 0x20, 0x6d, 0x6f, 0x6d])]; /// let form = MultipartForm::from_iter(parts); /// ``` + #[must_use] pub fn file(field_name: &str, file_name: &str, contents: Vec) -> Self { Self { name: field_name.to_owned(), diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index cf85dc532a..8d0871d55b 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -28,6 +28,7 @@ pub use self::typed::{SecondElementIs, TypedPath}; // Validates a path at compile time, used with the vpath macro. #[rustversion::since(1.80)] #[doc(hidden)] +#[must_use] pub const fn __private_validate_static_path(path: &'static str) -> &'static str { if path.is_empty() { panic!("Paths must start with a `/`. Use \"/\" for root routes") diff --git a/axum-extra/src/typed_header.rs b/axum-extra/src/typed_header.rs index 7c08be9e38..41cf91819e 100644 --- a/axum-extra/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -137,11 +137,13 @@ pub struct TypedHeaderRejection { impl TypedHeaderRejection { /// Name of the header that caused the rejection + #[must_use] pub fn name(&self) -> &http::header::HeaderName { self.name } /// Reason why the header extraction has failed + #[must_use] pub fn reason(&self) -> &TypedHeaderRejectionReason { &self.reason } diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 0f5efba326..1dda92354c 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -58,6 +58,7 @@ pub struct MatchedPath(pub(crate) Arc); impl MatchedPath { /// Returns a `str` representation of the path. + #[must_use] pub fn as_str(&self) -> &str { &self.0 } diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 22ab7fa47c..e9832e8e84 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -146,6 +146,7 @@ impl Field<'_> { /// The field name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() } @@ -153,16 +154,19 @@ impl Field<'_> { /// The file name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn file_name(&self) -> Option<&str> { self.inner.file_name() } /// Get the [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of the field. + #[must_use] pub fn content_type(&self) -> Option<&str> { self.inner.content_type().map(|m| m.as_ref()) } /// Get a map of headers as [`HeaderMap`]. + #[must_use] pub fn headers(&self) -> &HeaderMap { self.inner.headers() } @@ -238,11 +242,13 @@ impl MultipartError { } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.source.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { status_code_from_multer_error(&self.source) } diff --git a/axum/src/extract/nested_path.rs b/axum/src/extract/nested_path.rs index 2e58d0e826..1d2c255e04 100644 --- a/axum/src/extract/nested_path.rs +++ b/axum/src/extract/nested_path.rs @@ -41,6 +41,7 @@ pub struct NestedPath(Arc); impl NestedPath { /// Returns a `str` representation of the path. + #[must_use] pub fn as_str(&self) -> &str { &self.0 } diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index f37fff4dfb..c89a245b3a 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -407,16 +407,19 @@ pub struct FailedToDeserializePathParams(PathDeserializationError); impl FailedToDeserializePathParams { /// Get a reference to the underlying error kind. + #[must_use] pub fn kind(&self) -> &ErrorKind { &self.0.kind } /// Convert this error into the underlying error kind. + #[must_use] pub fn into_kind(self) -> ErrorKind { self.0.kind } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { match self.0.kind { ErrorKind::Message(_) @@ -432,6 +435,7 @@ impl FailedToDeserializePathParams { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> StatusCode { match self.0.kind { ErrorKind::Message(_) @@ -523,6 +527,7 @@ where impl RawPathParams { /// Get an iterator over the path parameters. + #[must_use] pub fn iter(&self) -> RawPathParamsIter<'_> { self.into_iter() } @@ -561,11 +566,13 @@ pub struct InvalidUtf8InPathParam { impl InvalidUtf8InPathParam { /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> StatusCode { StatusCode::BAD_REQUEST } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 94a6703903..0e53d18a39 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -580,6 +580,7 @@ pub struct Utf8Bytes(ts::Utf8Bytes); impl Utf8Bytes { /// Creates from a static str. #[inline] + #[must_use] pub const fn from_static(str: &'static str) -> Self { Self(ts::Utf8Bytes::from_static(str)) } diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index 4113c124e0..e33928cd26 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -56,11 +56,13 @@ impl Redirect { } /// Returns the HTTP status code of the `Redirect`. + #[must_use] pub fn status_code(&self) -> StatusCode { self.status_code } /// Returns the `Redirect`'s URI. + #[must_use] pub fn location(&self) -> &str { &self.location } diff --git a/axum/src/routing/method_filter.rs b/axum/src/routing/method_filter.rs index 040783ec33..bd4593e88a 100644 --- a/axum/src/routing/method_filter.rs +++ b/axum/src/routing/method_filter.rs @@ -58,6 +58,7 @@ impl MethodFilter { } /// Performs the OR operation between the [`MethodFilter`] in `self` with `other`. + #[must_use] pub const fn or(self, other: Self) -> Self { Self(self.0 | other.0) } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 42e46612eb..5dee1e47fd 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -703,6 +703,7 @@ impl MethodRouter<(), Infallible> { /// ``` /// /// [`MakeService`]: tower::make::MakeService + #[must_use] pub fn into_make_service(self) -> IntoMakeService { IntoMakeService::new(self.with_state(())) } @@ -736,6 +737,7 @@ impl MethodRouter<(), Infallible> { /// [`MakeService`]: tower::make::MakeService /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info #[cfg(feature = "tokio")] + #[must_use] pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo { IntoMakeServiceWithConnectInfo::new(self.with_state(())) } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index e54a00bb28..94fe8cac16 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -317,6 +317,7 @@ where } /// True if the router currently has at least one route added. + #[must_use] pub fn has_routes(&self) -> bool { self.inner.path_router.has_routes() } @@ -513,6 +514,7 @@ where /// /// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See /// that method for more details. + #[must_use] pub fn into_service(self) -> RouterIntoService { RouterIntoService { router: self, @@ -540,6 +542,7 @@ impl Router { /// ``` /// /// [`MakeService`]: tower::make::MakeService + #[must_use] pub fn into_make_service(self) -> IntoMakeService { // call `Router::with_state` such that everything is turned into `Route` eagerly // rather than doing that per request @@ -548,6 +551,7 @@ impl Router { #[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")] #[cfg(feature = "tokio")] + #[must_use] pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo { // call `Router::with_state` such that everything is turned into `Route` eagerly // rather than doing that per request diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index e7ba36e95e..d82933d0b8 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -50,18 +50,21 @@ impl TestClient { TestClient { client, addr } } + #[must_use] pub fn get(&self, url: &str) -> RequestBuilder { RequestBuilder { builder: self.client.get(format!("http://{}{url}", self.addr)), } } + #[must_use] pub fn head(&self, url: &str) -> RequestBuilder { RequestBuilder { builder: self.client.head(format!("http://{}{url}", self.addr)), } } + #[must_use] pub fn post(&self, url: &str) -> RequestBuilder { RequestBuilder { builder: self.client.post(format!("http://{}{url}", self.addr)), @@ -69,6 +72,7 @@ impl TestClient { } #[allow(dead_code)] + #[must_use] pub fn put(&self, url: &str) -> RequestBuilder { RequestBuilder { builder: self.client.put(format!("http://{}{url}", self.addr)), @@ -76,6 +80,7 @@ impl TestClient { } #[allow(dead_code)] + #[must_use] pub fn patch(&self, url: &str) -> RequestBuilder { RequestBuilder { builder: self.client.patch(format!("http://{}{url}", self.addr)), @@ -83,6 +88,7 @@ impl TestClient { } #[allow(dead_code)] + #[must_use] pub fn server_port(&self) -> u16 { self.addr.port() } From 8a561f3e4c20e3c216d8a0ae9305767c332f58a3 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 10:41:21 +0200 Subject: [PATCH 02/19] style: use `Self` to avoid unnecessary repetition In `impl`s, `Self` can be used instead of the struct/enum being implemented, to reduce unnecessary repetition. This commit uses `Self` as often as possible. This fixes the `use_self` clippy lint. --- Cargo.toml | 1 + axum-core/src/extract/option.rs | 4 +-- axum-core/src/extract/request_parts.rs | 4 +-- axum-extra/src/either.rs | 12 ++++---- axum-extra/src/extract/cookie/mod.rs | 4 +-- axum-extra/src/extract/cookie/private.rs | 2 +- axum-extra/src/extract/cookie/signed.rs | 2 +- axum-extra/src/extract/host.rs | 10 +++---- axum-extra/src/extract/query.rs | 6 ++-- axum-extra/src/extract/scheme.rs | 6 ++-- axum-extra/src/extract/with_rejection.rs | 6 ++-- axum-extra/src/protobuf.rs | 2 +- axum-extra/src/response/multiple.rs | 2 +- axum-macros/src/debug_handler.rs | 8 +++--- axum-macros/src/from_request/mod.rs | 28 +++++++++---------- axum-macros/src/with_position.rs | 6 ++-- axum/src/extract/connect_info.rs | 4 +-- axum/src/extract/original_uri.rs | 2 +- axum/src/extract/path/mod.rs | 20 ++++++------- axum/src/extract/query.rs | 2 +- axum/src/extract/ws.rs | 18 ++++++------ axum/src/form.rs | 2 +- axum/src/handler/service.rs | 6 ++-- axum/src/json.rs | 2 +- axum/src/middleware/map_request.rs | 2 +- axum/src/response/sse.rs | 24 ++++++++-------- axum/src/routing/method_filter.rs | 18 ++++++------ axum/src/routing/method_routing.rs | 26 ++++++++--------- axum/src/routing/mod.rs | 28 +++++++++---------- axum/src/routing/path_router.rs | 14 +++++----- axum/src/routing/route.rs | 2 +- .../test_helpers/counting_cloneable_state.rs | 4 +-- axum/src/test_helpers/test_client.rs | 2 +- 33 files changed, 137 insertions(+), 142 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce5f15c826..ea332d0b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ unnested_or_patterns = "warn" unused_self = "warn" verbose_file_reads = "warn" must_use_candidate = "warn" +use_self = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/extract/option.rs b/axum-core/src/extract/option.rs index c537e72187..ca4be459c8 100644 --- a/axum-core/src/extract/option.rs +++ b/axum-core/src/extract/option.rs @@ -45,7 +45,7 @@ where fn from_request_parts( parts: &mut Parts, state: &S, - ) -> impl Future, Self::Rejection>> { + ) -> impl Future> { T::from_request_parts(parts, state) } } @@ -57,7 +57,7 @@ where { type Rejection = T::Rejection; - async fn from_request(req: Request, state: &S) -> Result, Self::Rejection> { + async fn from_request(req: Request, state: &S) -> Result { T::from_request(req, state).await } } diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index b2a5c14aa8..c93a474964 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -73,7 +73,7 @@ where async fn from_request(req: Request, _: &S) -> Result { let mut body = req.into_limited_body(); - let mut bytes = BytesMut::new(); + let mut bytes = Self::new(); body_to_bytes_mut(&mut body, &mut bytes).await?; Ok(bytes) } @@ -128,7 +128,7 @@ where } })?; - let string = String::from_utf8(bytes.into()).map_err(InvalidUtf8::from_err)?; + let string = Self::from_utf8(bytes.into()).map_err(InvalidUtf8::from_err)?; Ok(string) } diff --git a/axum-extra/src/either.rs b/axum-extra/src/either.rs index 5d0e33eb7c..9f426c951d 100755 --- a/axum-extra/src/either.rs +++ b/axum-extra/src/either.rs @@ -283,8 +283,8 @@ where fn layer(&self, inner: S) -> Self::Service { match self { - Either::E1(layer) => Either::E1(layer.layer(inner)), - Either::E2(layer) => Either::E2(layer.layer(inner)), + Self::E1(layer) => Either::E1(layer.layer(inner)), + Self::E2(layer) => Either::E2(layer.layer(inner)), } } } @@ -300,15 +300,15 @@ where fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self { - Either::E1(inner) => inner.poll_ready(cx), - Either::E2(inner) => inner.poll_ready(cx), + Self::E1(inner) => inner.poll_ready(cx), + Self::E2(inner) => inner.poll_ready(cx), } } fn call(&mut self, req: R) -> Self::Future { match self { - Either::E1(inner) => futures_util::future::Either::Left(inner.call(req)), - Either::E2(inner) => futures_util::future::Either::Right(inner.call(req)), + Self::E1(inner) => futures_util::future::Either::Left(inner.call(req)), + Self::E2(inner) => futures_util::future::Either::Right(inner.call(req)), } } } diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 6b91bdb088..e99fa65eb0 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -324,13 +324,13 @@ mod tests { } impl FromRef for Key { - fn from_ref(state: &AppState) -> Key { + fn from_ref(state: &AppState) -> Self { state.key.clone() } } impl FromRef for CustomKey { - fn from_ref(state: &AppState) -> CustomKey { + fn from_ref(state: &AppState) -> Self { state.custom_key.clone() } } diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index 48ba753f6a..09ac5d7ec1 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -136,7 +136,7 @@ where key, _marker: _, } = PrivateCookieJar::from_headers(&parts.headers, key); - Ok(PrivateCookieJar { + Ok(Self { jar, key, _marker: PhantomData, diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 662638c8c3..1c2c97f8ea 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -153,7 +153,7 @@ where key, _marker: _, } = SignedCookieJar::from_headers(&parts.headers, key); - Ok(SignedCookieJar { + Ok(Self { jar, key, _marker: PhantomData, diff --git a/axum-extra/src/extract/host.rs b/axum-extra/src/extract/host.rs index e9eb91c5be..0f33a4ab02 100644 --- a/axum-extra/src/extract/host.rs +++ b/axum-extra/src/extract/host.rs @@ -36,7 +36,7 @@ where async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { parts - .extract::>() + .extract::>() .await .ok() .flatten() @@ -55,7 +55,7 @@ where _state: &S, ) -> Result, Self::Rejection> { if let Some(host) = parse_forwarded(&parts.headers) { - return Ok(Some(Host(host.to_owned()))); + return Ok(Some(Self(host.to_owned()))); } if let Some(host) = parts @@ -63,7 +63,7 @@ where .get(X_FORWARDED_HOST_HEADER_KEY) .and_then(|host| host.to_str().ok()) { - return Ok(Some(Host(host.to_owned()))); + return Ok(Some(Self(host.to_owned()))); } if let Some(host) = parts @@ -71,11 +71,11 @@ where .get(http::header::HOST) .and_then(|host| host.to_str().ok()) { - return Ok(Some(Host(host.to_owned()))); + return Ok(Some(Self(host.to_owned()))); } if let Some(authority) = parts.uri.authority() { - return Ok(Some(Host(parse_authority(authority).to_owned()))); + return Ok(Some(Self(parse_authority(authority).to_owned()))); } Ok(None) diff --git a/axum-extra/src/extract/query.rs b/axum-extra/src/extract/query.rs index 72bf2f4703..3b0330acfb 100644 --- a/axum-extra/src/extract/query.rs +++ b/axum-extra/src/extract/query.rs @@ -91,7 +91,7 @@ where serde_html_form::Deserializer::new(form_urlencoded::parse(query.as_bytes())); let value = serde_path_to_error::deserialize(deserializer) .map_err(FailedToDeserializeQueryString::from_err)?; - Ok(Query(value)) + Ok(Self(value)) } } @@ -170,9 +170,9 @@ where serde_html_form::Deserializer::new(form_urlencoded::parse(query.as_bytes())); let value = serde_path_to_error::deserialize(deserializer) .map_err(FailedToDeserializeQueryString::from_err)?; - Ok(OptionalQuery(Some(value))) + Ok(Self(Some(value))) } else { - Ok(OptionalQuery(None)) + Ok(Self(None)) } } } diff --git a/axum-extra/src/extract/scheme.rs b/axum-extra/src/extract/scheme.rs index b20e9cf205..3634bb262f 100644 --- a/axum-extra/src/extract/scheme.rs +++ b/axum-extra/src/extract/scheme.rs @@ -38,7 +38,7 @@ where async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { // Within Forwarded header if let Some(scheme) = parse_forwarded(&parts.headers) { - return Ok(Scheme(scheme.to_owned())); + return Ok(Self(scheme.to_owned())); } // X-Forwarded-Proto @@ -47,12 +47,12 @@ where .get(X_FORWARDED_PROTO_HEADER_KEY) .and_then(|scheme| scheme.to_str().ok()) { - return Ok(Scheme(scheme.to_owned())); + return Ok(Self(scheme.to_owned())); } // From parts of an HTTP/2 request if let Some(scheme) = parts.uri.scheme_str() { - return Ok(Scheme(scheme.to_owned())); + return Ok(Self(scheme.to_owned())); } Err(SchemeMissing) diff --git a/axum-extra/src/extract/with_rejection.rs b/axum-extra/src/extract/with_rejection.rs index c093f6fa47..ee9906682a 100644 --- a/axum-extra/src/extract/with_rejection.rs +++ b/axum-extra/src/extract/with_rejection.rs @@ -119,7 +119,7 @@ where async fn from_request(req: Request, state: &S) -> Result { let extractor = E::from_request(req, state).await?; - Ok(WithRejection(extractor, PhantomData)) + Ok(Self(extractor, PhantomData)) } } @@ -133,7 +133,7 @@ where async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let extractor = E::from_request_parts(parts, state).await?; - Ok(WithRejection(extractor, PhantomData)) + Ok(Self(extractor, PhantomData)) } } @@ -188,7 +188,7 @@ mod tests { impl From<()> for TestRejection { fn from(_: ()) -> Self { - TestRejection + Self } } diff --git a/axum-extra/src/protobuf.rs b/axum-extra/src/protobuf.rs index fb63c7a41c..260de04114 100644 --- a/axum-extra/src/protobuf.rs +++ b/axum-extra/src/protobuf.rs @@ -109,7 +109,7 @@ where .aggregate(); match T::decode(&mut buf) { - Ok(value) => Ok(Protobuf(value)), + Ok(value) => Ok(Self(value)), Err(err) => Err(ProtobufDecodeError::from_err(err).into()), } } diff --git a/axum-extra/src/response/multiple.rs b/axum-extra/src/response/multiple.rs index ce3fdd6b46..61bc1c75a9 100644 --- a/axum-extra/src/response/multiple.rs +++ b/axum-extra/src/response/multiple.rs @@ -26,7 +26,7 @@ impl MultipartForm { /// ``` #[must_use] pub fn with_parts(parts: Vec) -> Self { - MultipartForm { parts } + Self { parts } } } diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 73dd9f23d9..63151575e3 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -97,8 +97,8 @@ pub(crate) enum FunctionKind { impl fmt::Display for FunctionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FunctionKind::Handler => f.write_str("handler"), - FunctionKind::Middleware => f.write_str("middleware"), + Self::Handler => f.write_str("handler"), + Self::Middleware => f.write_str("middleware"), } } } @@ -106,8 +106,8 @@ impl fmt::Display for FunctionKind { impl FunctionKind { fn name_uppercase_plural(&self) -> &'static str { match self { - FunctionKind::Handler => "Handlers", - FunctionKind::Middleware => "Middleware", + Self::Handler => "Handlers", + Self::Middleware => "Middleware", } } } diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index 3838636597..451ba9bfb1 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -21,8 +21,8 @@ pub(crate) enum Trait { impl Trait { fn via_marker_type(&self) -> Option { match self { - Trait::FromRequest => Some(parse_quote!(M)), - Trait::FromRequestParts => None, + Self::FromRequest => Some(parse_quote!(M)), + Self::FromRequestParts => None, } } } @@ -30,8 +30,8 @@ impl Trait { impl fmt::Display for Trait { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Trait::FromRequest => f.write_str("FromRequest"), - Trait::FromRequestParts => f.write_str("FromRequestParts"), + Self::FromRequest => f.write_str("FromRequest"), + Self::FromRequestParts => f.write_str("FromRequestParts"), } } } @@ -50,9 +50,9 @@ impl State { /// ``` fn impl_generics(&self) -> impl Iterator { match self { - State::Default(inner) => Some(inner.clone()), - State::Custom(_) => None, - State::CannotInfer => Some(parse_quote!(S)), + Self::Default(inner) => Some(inner.clone()), + Self::Custom(_) => None, + Self::CannotInfer => Some(parse_quote!(S)), } .into_iter() } @@ -63,18 +63,18 @@ impl State { /// ``` fn trait_generics(&self) -> impl Iterator { match self { - State::Default(inner) | State::Custom(inner) => iter::once(inner.clone()), - State::CannotInfer => iter::once(parse_quote!(S)), + Self::Default(inner) | Self::Custom(inner) => iter::once(inner.clone()), + Self::CannotInfer => iter::once(parse_quote!(S)), } } fn bounds(&self) -> TokenStream { match self { - State::Custom(_) => quote! {}, - State::Default(inner) => quote! { + Self::Custom(_) => quote! {}, + Self::Default(inner) => quote! { #inner: ::std::marker::Send + ::std::marker::Sync, }, - State::CannotInfer => quote! { + Self::CannotInfer => quote! { S: ::std::marker::Send + ::std::marker::Sync, }, } @@ -84,8 +84,8 @@ impl State { impl ToTokens for State { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - State::Custom(inner) | State::Default(inner) => inner.to_tokens(tokens), - State::CannotInfer => quote! { S }.to_tokens(tokens), + Self::Custom(inner) | Self::Default(inner) => inner.to_tokens(tokens), + Self::CannotInfer => quote! { S }.to_tokens(tokens), } } } diff --git a/axum-macros/src/with_position.rs b/axum-macros/src/with_position.rs index e064a3f01e..b5c1a60922 100644 --- a/axum-macros/src/with_position.rs +++ b/axum-macros/src/with_position.rs @@ -40,8 +40,8 @@ impl WithPosition where I: Iterator, { - pub(crate) fn new(iter: impl IntoIterator) -> WithPosition { - WithPosition { + pub(crate) fn new(iter: impl IntoIterator) -> Self { + Self { handled_first: false, peekable: iter.into_iter().fuse().peekable(), } @@ -72,7 +72,7 @@ pub(crate) enum Position { impl Position { pub(crate) fn into_inner(self) -> T { match self { - Position::First(x) | Position::Middle(x) | Position::Last(x) | Position::Only(x) => x, + Self::First(x) | Self::Middle(x) | Self::Last(x) | Self::Only(x) => x, } } } diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index 2c7866f9f6..c0d471d6f9 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -105,8 +105,8 @@ const _: () = { } }; -impl Connected for SocketAddr { - fn connect_info(remote_addr: SocketAddr) -> Self { +impl Connected for SocketAddr { + fn connect_info(remote_addr: Self) -> Self { remote_addr } } diff --git a/axum/src/extract/original_uri.rs b/axum/src/extract/original_uri.rs index 35364281ba..b5dbb58347 100644 --- a/axum/src/extract/original_uri.rs +++ b/axum/src/extract/original_uri.rs @@ -76,7 +76,7 @@ where async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let uri = Extension::::from_request_parts(parts, state) .await - .unwrap_or_else(|_| Extension(OriginalUri(parts.uri.clone()))) + .unwrap_or_else(|_| Extension(Self(parts.uri.clone()))) .0; Ok(uri) } diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index c89a245b3a..0661dfd1c8 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -183,7 +183,7 @@ where } match T::deserialize(de::PathDeserializer::new(get_params(parts)?)) { - Ok(val) => Ok(Path(val)), + Ok(val) => Ok(Self(val)), Err(e) => Err(failed_to_deserialize_path_params(e)), } } @@ -356,9 +356,9 @@ pub enum ErrorKind { impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ErrorKind::Message(error) => error.fmt(f), - ErrorKind::InvalidUtf8InPathParam { key } => write!(f, "Invalid UTF-8 in `{key}`"), - ErrorKind::WrongNumberOfParameters { got, expected } => { + Self::Message(error) => error.fmt(f), + Self::InvalidUtf8InPathParam { key } => write!(f, "Invalid UTF-8 in `{key}`"), + Self::WrongNumberOfParameters { got, expected } => { write!( f, "Wrong number of path arguments for `Path`. Expected {expected} but got {got}" @@ -370,8 +370,8 @@ impl fmt::Display for ErrorKind { Ok(()) } - ErrorKind::UnsupportedType { name } => write!(f, "Unsupported type `{name}`"), - ErrorKind::ParseErrorAtKey { + Self::UnsupportedType { name } => write!(f, "Unsupported type `{name}`"), + Self::ParseErrorAtKey { key, value, expected_type, @@ -379,11 +379,11 @@ impl fmt::Display for ErrorKind { f, "Cannot parse `{key}` with value `{value}` to a `{expected_type}`" ), - ErrorKind::ParseError { + Self::ParseError { value, expected_type, } => write!(f, "Cannot parse `{value}` to a `{expected_type}`"), - ErrorKind::ParseErrorAtIndex { + Self::ParseErrorAtIndex { index, value, expected_type, @@ -391,7 +391,7 @@ impl fmt::Display for ErrorKind { f, "Cannot parse value at index {index} with value `{value}` to a `{expected_type}`" ), - ErrorKind::DeserializeError { + Self::DeserializeError { key, value, message, @@ -773,7 +773,7 @@ mod tests { D: serde::Deserializer<'de>, { let s = <&str as serde::Deserialize>::deserialize(deserializer)?; - Ok(Param(s.to_owned())) + Ok(Self(s.to_owned())) } } diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index 58b7d366e8..b06c7f3e53 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -91,7 +91,7 @@ where serde_urlencoded::Deserializer::new(form_urlencoded::parse(query.as_bytes())); let params = serde_path_to_error::deserialize(deserializer) .map_err(FailedToDeserializeQueryString::from_err)?; - Ok(Query(params)) + Ok(Self(params)) } } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 0e53d18a39..e3992d10c8 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -833,49 +833,49 @@ impl Message { } /// Create a new text WebSocket message from a stringable. - pub fn text(string: S) -> Message + pub fn text(string: S) -> Self where S: Into, { - Message::Text(string.into()) + Self::Text(string.into()) } /// Create a new binary WebSocket message by converting to `Bytes`. - pub fn binary(bin: B) -> Message + pub fn binary(bin: B) -> Self where B: Into, { - Message::Binary(bin.into()) + Self::Binary(bin.into()) } } impl From for Message { fn from(string: String) -> Self { - Message::Text(string.into()) + Self::Text(string.into()) } } impl<'s> From<&'s str> for Message { fn from(string: &'s str) -> Self { - Message::Text(string.into()) + Self::Text(string.into()) } } impl<'b> From<&'b [u8]> for Message { fn from(data: &'b [u8]) -> Self { - Message::Binary(Bytes::copy_from_slice(data)) + Self::Binary(Bytes::copy_from_slice(data)) } } impl From for Message { fn from(data: Bytes) -> Self { - Message::Binary(data) + Self::Binary(data) } } impl From> for Message { fn from(data: Vec) -> Self { - Message::Binary(data.into()) + Self::Binary(data.into()) } } diff --git a/axum/src/form.rs b/axum/src/form.rs index dabfb65332..eb499def73 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -95,7 +95,7 @@ where } }, )?; - Ok(Form(value)) + Ok(Self(value)) } Err(RawFormRejection::BytesRejection(r)) => Err(FormRejection::BytesRejection(r)), Err(RawFormRejection::InvalidFormContentType(r)) => { diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 2090051978..1bfdeb4ecb 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -60,7 +60,7 @@ impl HandlerService { /// ``` /// /// [`MakeService`]: tower::make::MakeService - pub fn into_make_service(self) -> IntoMakeService> { + pub fn into_make_service(self) -> IntoMakeService { IntoMakeService::new(self) } @@ -101,9 +101,7 @@ impl HandlerService { /// [`MakeService`]: tower::make::MakeService /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info #[cfg(feature = "tokio")] - pub fn into_make_service_with_connect_info( - self, - ) -> IntoMakeServiceWithConnectInfo, C> { + pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo { IntoMakeServiceWithConnectInfo::new(self) } } diff --git a/axum/src/json.rs b/axum/src/json.rs index 1e662cc6d1..00126f09b5 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -184,7 +184,7 @@ where let deserializer = &mut serde_json::Deserializer::from_slice(bytes); match serde_path_to_error::deserialize(deserializer) { - Ok(value) => Ok(Json(value)), + Ok(value) => Ok(Self(value)), Err(err) => Err(make_rejection(err)), } } diff --git a/axum/src/middleware/map_request.rs b/axum/src/middleware/map_request.rs index 56f250bc4d..3a90bc65ac 100644 --- a/axum/src/middleware/map_request.rs +++ b/axum/src/middleware/map_request.rs @@ -380,7 +380,7 @@ where } impl IntoMapRequestResult for Request { - fn into_map_request_result(self) -> Result, Response> { + fn into_map_request_result(self) -> Result { Ok(self) } } diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 933f115e6f..d519b0d42f 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -63,7 +63,7 @@ impl Sse { S: TryStream + Send + 'static, S::Error: Into, { - Sse { stream } + Self { stream } } /// Configure the interval between keep-alive messages. @@ -154,12 +154,12 @@ impl Buffer { /// a new active buffer with the previous contents. fn as_mut(&mut self) -> &mut BytesMut { match self { - Buffer::Active(bytes_mut) => bytes_mut, - Buffer::Finalized(bytes) => { - *self = Buffer::Active(BytesMut::from(mem::take(bytes))); + Self::Active(bytes_mut) => bytes_mut, + Self::Finalized(bytes) => { + *self = Self::Active(BytesMut::from(mem::take(bytes))); match self { - Buffer::Active(bytes_mut) => bytes_mut, - Buffer::Finalized(_) => unreachable!(), + Self::Active(bytes_mut) => bytes_mut, + Self::Finalized(_) => unreachable!(), } } } @@ -199,7 +199,7 @@ impl Event { /// - Panics if `data` or `json_data` have already been called. /// /// [`MessageEvent`'s data field]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data - pub fn data(mut self, data: T) -> Event + pub fn data(mut self, data: T) -> Self where T: AsRef, { @@ -226,7 +226,7 @@ impl Event { /// /// [`MessageEvent`'s data field]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data #[cfg(feature = "json")] - pub fn json_data(mut self, data: T) -> Result + pub fn json_data(mut self, data: T) -> Result where T: serde::Serialize, { @@ -271,7 +271,7 @@ impl Event { /// /// Panics if `comment` contains any newlines or carriage returns, as they are not allowed in /// comments. - pub fn comment(mut self, comment: T) -> Event + pub fn comment(mut self, comment: T) -> Self where T: AsRef, { @@ -293,7 +293,7 @@ impl Event { /// /// - Panics if `event` contains any newlines or carriage returns. /// - Panics if this function has already been called on this event. - pub fn event(mut self, event: T) -> Event + pub fn event(mut self, event: T) -> Self where T: AsRef, { @@ -316,7 +316,7 @@ impl Event { /// # Panics /// /// Panics if this function has already been called on this event. - pub fn retry(mut self, duration: Duration) -> Event { + pub fn retry(mut self, duration: Duration) -> Self { if self.flags.contains(EventFlags::HAS_RETRY) { panic!("Called `Event::retry` multiple times"); } @@ -360,7 +360,7 @@ impl Event { /// /// - Panics if `id` contains any newlines, carriage returns or null characters. /// - Panics if this function has already been called on this event. - pub fn id(mut self, id: T) -> Event + pub fn id(mut self, id: T) -> Self where T: AsRef, { diff --git a/axum/src/routing/method_filter.rs b/axum/src/routing/method_filter.rs index bd4593e88a..4b3d142356 100644 --- a/axum/src/routing/method_filter.rs +++ b/axum/src/routing/method_filter.rs @@ -90,15 +90,15 @@ impl TryFrom for MethodFilter { fn try_from(m: Method) -> Result { match m { - Method::CONNECT => Ok(MethodFilter::CONNECT), - Method::DELETE => Ok(MethodFilter::DELETE), - Method::GET => Ok(MethodFilter::GET), - Method::HEAD => Ok(MethodFilter::HEAD), - Method::OPTIONS => Ok(MethodFilter::OPTIONS), - Method::PATCH => Ok(MethodFilter::PATCH), - Method::POST => Ok(MethodFilter::POST), - Method::PUT => Ok(MethodFilter::PUT), - Method::TRACE => Ok(MethodFilter::TRACE), + Method::CONNECT => Ok(Self::CONNECT), + Method::DELETE => Ok(Self::DELETE), + Method::GET => Ok(Self::GET), + Method::HEAD => Ok(Self::HEAD), + Method::OPTIONS => Ok(Self::OPTIONS), + Method::PATCH => Ok(Self::PATCH), + Method::POST => Ok(Self::POST), + Method::PUT => Ok(Self::PUT), + Method::TRACE => Ok(Self::TRACE), other => Err(NoMatchingMethodFilter { method: other }), } } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 5dee1e47fd..4a39542bd1 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -571,14 +571,14 @@ enum AllowHeader { impl AllowHeader { fn merge(self, other: Self) -> Self { match (self, other) { - (AllowHeader::Skip, _) | (_, AllowHeader::Skip) => AllowHeader::Skip, - (AllowHeader::None, AllowHeader::None) => AllowHeader::None, - (AllowHeader::None, AllowHeader::Bytes(pick)) => AllowHeader::Bytes(pick), - (AllowHeader::Bytes(pick), AllowHeader::None) => AllowHeader::Bytes(pick), - (AllowHeader::Bytes(mut a), AllowHeader::Bytes(b)) => { + (Self::Skip, _) | (_, Self::Skip) => Self::Skip, + (Self::None, Self::None) => Self::None, + (Self::None, Self::Bytes(pick)) => Self::Bytes(pick), + (Self::Bytes(pick), Self::None) => Self::Bytes(pick), + (Self::Bytes(mut a), Self::Bytes(b)) => { a.extend_from_slice(b","); a.extend_from_slice(&b); - AllowHeader::Bytes(a) + Self::Bytes(a) } } } @@ -994,7 +994,7 @@ where #[doc = include_str!("../docs/method_routing/route_layer.md")] #[track_caller] - pub fn route_layer(mut self, layer: L) -> MethodRouter + pub fn route_layer(mut self, layer: L) -> Self where L: Layer> + Clone + Send + Sync + 'static, L::Service: Service + Clone + Send + Sync + 'static, @@ -1037,7 +1037,7 @@ where pub(crate) fn merge_for_path( mut self, path: Option<&str>, - other: MethodRouter, + other: Self, ) -> Result> { // written using inner functions to generate less IR fn merge_inner( @@ -1088,7 +1088,7 @@ where #[doc = include_str!("../docs/method_routing/merge.md")] #[track_caller] - pub fn merge(self, other: MethodRouter) -> Self { + pub fn merge(self, other: Self) -> Self { match self.merge_for_path(None, other) { Ok(t) => t, // not using unwrap or unwrap_or_else to get a clean panic message + the right location @@ -1256,11 +1256,9 @@ where fn with_state(self, state: &S) -> MethodEndpoint { match self { - MethodEndpoint::None => MethodEndpoint::None, - MethodEndpoint::Route(route) => MethodEndpoint::Route(route), - MethodEndpoint::BoxedHandler(handler) => { - MethodEndpoint::Route(handler.into_route(state.clone())) - } + Self::None => MethodEndpoint::None, + Self::Route(route) => MethodEndpoint::Route(route), + Self::BoxedHandler(handler) => MethodEndpoint::Route(handler.into_route(state.clone())), } } } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 94fe8cac16..ce2425f1bc 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -187,7 +187,7 @@ where T::Response: IntoResponse, T::Future: Send + 'static, { - let service = match try_downcast::, _>(service) { + let service = match try_downcast::(service) { Ok(_) => { panic!( "Invalid route: `Router::route_service` cannot be used with `Router`s. \ @@ -205,7 +205,7 @@ where #[doc = include_str!("../docs/routing/nest.md")] #[doc(alias = "scope")] // Some web frameworks like actix-web use this term #[track_caller] - pub fn nest(self, path: &str, router: Router) -> Self { + pub fn nest(self, path: &str, router: Self) -> Self { if path.is_empty() || path == "/" { panic!("Nesting at the root is no longer supported. Use merge instead."); } @@ -245,9 +245,9 @@ where #[track_caller] pub fn merge(self, other: R) -> Self where - R: Into>, + R: Into, { - let other: Router = other.into(); + let other: Self = other.into(); let RouterInner { path_router, default_fallback, @@ -284,7 +284,7 @@ where } #[doc = include_str!("../docs/routing/layer.md")] - pub fn layer(self, layer: L) -> Router + pub fn layer(self, layer: L) -> Self where L: Layer + Clone + Send + Sync + 'static, L::Service: Service + Clone + Send + Sync + 'static, @@ -729,16 +729,16 @@ where fn with_state(self, state: S) -> Fallback { match self { - Fallback::Default(route) => Fallback::Default(route), - Fallback::Service(route) => Fallback::Service(route), - Fallback::BoxedHandler(handler) => Fallback::Service(handler.into_route(state)), + Self::Default(route) => Fallback::Default(route), + Self::Service(route) => Fallback::Service(route), + Self::BoxedHandler(handler) => Fallback::Service(handler.into_route(state)), } } fn call_with_state(self, req: Request, state: S) -> RouteFuture { match self { - Fallback::Default(route) | Fallback::Service(route) => route.oneshot_inner_owned(req), - Fallback::BoxedHandler(handler) => { + Self::Default(route) | Self::Service(route) => route.oneshot_inner_owned(req), + Self::BoxedHandler(handler) => { let route = handler.clone().into_route(state); route.oneshot_inner_owned(req) } @@ -776,7 +776,7 @@ impl Endpoint where S: Clone + Send + Sync + 'static, { - fn layer(self, layer: L) -> Endpoint + fn layer(self, layer: L) -> Self where L: Layer + Clone + Send + Sync + 'static, L::Service: Service + Clone + Send + Sync + 'static, @@ -785,10 +785,8 @@ where >::Future: Send + 'static, { match self { - Endpoint::MethodRouter(method_router) => { - Endpoint::MethodRouter(method_router.layer(layer)) - } - Endpoint::Route(route) => Endpoint::Route(route.layer(layer)), + Self::MethodRouter(method_router) => Self::MethodRouter(method_router.layer(layer)), + Self::Route(route) => Self::Route(route.layer(layer)), } } } diff --git a/axum/src/routing/path_router.rs b/axum/src/routing/path_router.rs index 263cc032f7..5999528193 100644 --- a/axum/src/routing/path_router.rs +++ b/axum/src/routing/path_router.rs @@ -143,8 +143,8 @@ where .map_err(|err| format!("Invalid route {path:?}: {err}")) } - pub(super) fn merge(&mut self, other: PathRouter) -> Result<(), Cow<'static, str>> { - let PathRouter { + pub(super) fn merge(&mut self, other: Self) -> Result<(), Cow<'static, str>> { + let Self { routes, node, prev_route_id: _, @@ -172,11 +172,11 @@ where pub(super) fn nest( &mut self, path_to_nest_at: &str, - router: PathRouter, + router: Self, ) -> Result<(), Cow<'static, str>> { let prefix = validate_nest_path(self.v7_checks, path_to_nest_at); - let PathRouter { + let Self { routes, node, prev_route_id: _, @@ -248,7 +248,7 @@ where Ok(()) } - pub(super) fn layer(self, layer: L) -> PathRouter + pub(super) fn layer(self, layer: L) -> Self where L: Layer + Clone + Send + Sync + 'static, L::Service: Service + Clone + Send + Sync + 'static, @@ -265,7 +265,7 @@ where }) .collect(); - PathRouter { + Self { routes, node: self.node, prev_route_id: self.prev_route_id, @@ -298,7 +298,7 @@ where }) .collect(); - PathRouter { + Self { routes, node: self.node, prev_route_id: self.prev_route_id, diff --git a/axum/src/routing/route.rs b/axum/src/routing/route.rs index 6cdc58a617..6730078787 100644 --- a/axum/src/routing/route.rs +++ b/axum/src/routing/route.rs @@ -59,7 +59,7 @@ impl Route { pub(crate) fn layer(self, layer: L) -> Route where - L: Layer> + Clone + Send + 'static, + L: Layer + Clone + Send + 'static, L::Service: Service + Clone + Send + Sync + 'static, >::Response: IntoResponse + 'static, >::Error: Into + 'static, diff --git a/axum/src/test_helpers/counting_cloneable_state.rs b/axum/src/test_helpers/counting_cloneable_state.rs index 762d5ce972..f428e737f6 100644 --- a/axum/src/test_helpers/counting_cloneable_state.rs +++ b/axum/src/test_helpers/counting_cloneable_state.rs @@ -18,7 +18,7 @@ impl CountingCloneableState { setup_done: AtomicBool::new(false), count: AtomicUsize::new(0), }; - CountingCloneableState { + Self { state: Arc::new(inner_state), } } @@ -47,6 +47,6 @@ impl Clone for CountingCloneableState { state.count.fetch_add(1, Ordering::SeqCst); } - CountingCloneableState { state } + Self { state } } } diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index d82933d0b8..408960e186 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -47,7 +47,7 @@ impl TestClient { .build() .unwrap(); - TestClient { client, addr } + Self { client, addr } } #[must_use] From 276e92431298b336e21d94c2647e4115b9d5d24f Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 11:13:47 +0200 Subject: [PATCH 03/19] style: mark `request_*` modules as private The `request_parts` and `request` modules were marked as `pub(crate)`, which is misleading since the public parts are re-exported by `lib.rs` anyway. Attempting to mark them as `pub`, like suggested by the `redundant_pub_crate` clippy lint causes another Rust-native lint to appear: `unreachable-pub`. To avoid both of these lints, it's best to simply mark the whole module as private, and re-export the public traits separately. Since these public traits are re-exported by `lib.rs` anyway, this does not affect the public API. --- axum-core/src/ext_traits/mod.rs | 7 +++++-- axum-core/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/axum-core/src/ext_traits/mod.rs b/axum-core/src/ext_traits/mod.rs index 951a12d70c..b7a21b085e 100644 --- a/axum-core/src/ext_traits/mod.rs +++ b/axum-core/src/ext_traits/mod.rs @@ -1,5 +1,8 @@ -pub(crate) mod request; -pub(crate) mod request_parts; +mod request; +mod request_parts; + +pub use request::RequestExt; +pub use request_parts::RequestPartsExt; #[cfg(test)] mod tests { diff --git a/axum-core/src/lib.rs b/axum-core/src/lib.rs index 1fd4534710..39b1712467 100644 --- a/axum-core/src/lib.rs +++ b/axum-core/src/lib.rs @@ -30,7 +30,7 @@ pub mod response; /// Alias for a type-erased error type. pub type BoxError = Box; -pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt}; +pub use self::ext_traits::{RequestExt, RequestPartsExt}; #[cfg(test)] use axum_macros::__private_axum_test as test; From 3b62dfdfdbf3db2861f0dcb99cd4253067f73635 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:04:47 +0200 Subject: [PATCH 04/19] style: remove redundant else block The `else` block adds unnecessary indentation and verbosity. To avoid this, we can simply move `return None` statement outside the `if` statement. This fixes the `redundant_else` clippy lint. --- Cargo.toml | 1 + axum-macros/src/debug_handler.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea332d0b9f..3a1410be55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ unused_self = "warn" verbose_file_reads = "warn" must_use_candidate = "warn" use_self = "warn" +redundant_else = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 63151575e3..ee1728b130 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -536,9 +536,8 @@ fn check_input_order(item_fn: &ItemFn, kind: FunctionKind) -> Option compile_error!(#error); }); - } else { - return None; } + return None; } if types_that_consume_the_request.len() == 2 { From a0f8a48a2a55f6237fbe299af715e17557d84bea Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:11:21 +0200 Subject: [PATCH 05/19] style: remove unnecessary semicolon Semicolons at the end of if statements evaluating to `()` are unnecessary and may be removed to avoid confusion and visual clutter. This fixes the `unnecessary_semicolon` clippy lint. --- Cargo.toml | 1 + axum-macros/src/debug_handler.rs | 2 +- axum/src/extract/nested_path.rs | 2 +- axum/src/extract/path/de.rs | 2 +- axum/src/routing/mod.rs | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a1410be55..a2c3bae55d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ verbose_file_reads = "warn" must_use_candidate = "warn" use_self = "warn" redundant_else = "warn" +unnecessary_semicolon = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index ee1728b130..a7f452b2e9 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -522,7 +522,7 @@ fn check_input_order(item_fn: &ItemFn, kind: FunctionKind) -> Option Deserializer<'de> for ValueDeserializer<'de> { )); } None => {} - }; + } self.value .take() diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index ce2425f1bc..416b4ef480 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -270,7 +270,7 @@ where (false, false) => { panic!("Cannot merge two `Router`s that both have a fallback") } - }; + } panic_on_err!(this.path_router.merge(path_router)); From ddf5830e8450c5c0d909b5c2b97e42c4b1d7313a Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:19:56 +0200 Subject: [PATCH 06/19] style: use standard `let..else` construct `let...else` provides a standard construct for this same pattern that people can easily recognize (and is IMO much less confusing). It's also more compact. This fixes the `manual_let_else` clippy lint. --- Cargo.toml | 1 + axum-extra/src/extract/json_deserializer.rs | 12 +++--------- axum-macros/src/from_request/mod.rs | 8 ++------ axum/src/extract/matched_path.rs | 4 +--- axum/src/extract/mod.rs | 8 ++------ axum/src/extract/ws.rs | 4 +--- 6 files changed, 10 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a2c3bae55d..4949916d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ must_use_candidate = "warn" use_self = "warn" redundant_else = "warn" unnecessary_semicolon = "warn" +manual_let_else = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-extra/src/extract/json_deserializer.rs b/axum-extra/src/extract/json_deserializer.rs index 051ab0f1bd..8d53101999 100644 --- a/axum-extra/src/extract/json_deserializer.rs +++ b/axum-extra/src/extract/json_deserializer.rs @@ -183,21 +183,15 @@ composite_rejection! { } fn json_content_type(headers: &HeaderMap) -> bool { - let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { - content_type - } else { + let Some(content_type) = headers.get(header::CONTENT_TYPE) else { return false; }; - let content_type = if let Ok(content_type) = content_type.to_str() { - content_type - } else { + let Ok(content_type) = content_type.to_str() else { return false; }; - let mime = if let Ok(mime) = content_type.parse::() { - mime - } else { + let Ok(mime) = content_type.parse::() else { return false; }; diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index 451ba9bfb1..cc8bca7bb2 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -642,9 +642,7 @@ fn extract_fields( } fn peel_option(ty: &syn::Type) -> Option<&syn::Type> { - let type_path = if let syn::Type::Path(type_path) = ty { - type_path - } else { + let syn::Type::Path(type_path) = ty else { return None; }; @@ -673,9 +671,7 @@ fn peel_option(ty: &syn::Type) -> Option<&syn::Type> { } fn peel_result_ok(ty: &syn::Type) -> Option<&syn::Type> { - let type_path = if let syn::Type::Path(type_path) = ty { - type_path - } else { + let syn::Type::Path(type_path) = ty else { return None; }; diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 1dda92354c..eb6ef49929 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -103,9 +103,7 @@ pub(crate) fn set_matched_path_for_request( route_id_to_path: &HashMap>, extensions: &mut http::Extensions, ) { - let matched_path = if let Some(matched_path) = route_id_to_path.get(&id) { - matched_path - } else { + let Some(matched_path) = route_id_to_path.get(&id) else { #[cfg(debug_assertions)] panic!("should always have a matched path for a route id"); #[cfg(not(debug_assertions))] diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index 8a0af3da49..d2b19b684d 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -81,15 +81,11 @@ pub use self::ws::WebSocketUpgrade; // this is duplicated in `axum-extra/src/extract/form.rs` pub(super) fn has_content_type(headers: &HeaderMap, expected_content_type: &mime::Mime) -> bool { - let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { - content_type - } else { + let Some(content_type) = headers.get(header::CONTENT_TYPE) else { return false; }; - let content_type = if let Ok(content_type) = content_type.to_str() { - content_type - } else { + let Ok(content_type) = content_type.to_str() else { return false; }; diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index e3992d10c8..60e5c4b308 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -487,9 +487,7 @@ fn header_eq(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool } fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - let header = if let Some(header) = headers.get(&key) { - header - } else { + let Some(header) = headers.get(&key) else { return false; }; From d8b6ef2e09ebaa226e5edf9f59a9ce015f507ce3 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:48:16 +0200 Subject: [PATCH 07/19] style: use the terser `Option::map_or*` methods `if let Some(v) = ... { y } else { x }` and `match .. { Some(v) => y, None/_ => x }` are more idiomatically done with the `Option::map_or*` methods. Using the dedicated methods results in both clearer and more concise code than using an `if let` expression. This fixes the `option_if_let_else` clippy lint. NOTE: Clippy's suggested method for `try_downcast` couldn't compile properly, so I rewrote the `if let` statement slightly using `Option::take`. --- Cargo.toml | 1 + axum-core/src/body.rs | 9 +- axum-extra/src/extract/multipart.rs | 6 +- axum-extra/src/response/attachment.rs | 31 +++--- axum-extra/src/routing/mod.rs | 9 +- axum-macros/src/debug_handler.rs | 110 ++++++++++---------- axum-macros/src/from_ref.rs | 29 +++--- axum-macros/src/from_request/mod.rs | 125 +++++++++++------------ axum-macros/src/typed_path.rs | 48 ++++----- axum/src/extract/matched_path.rs | 23 +++-- axum/src/extract/multipart.rs | 17 +-- axum/src/extract/nested_path.rs | 10 +- axum/src/extract/ws.rs | 59 +++++------ axum/src/routing/method_routing.rs | 17 +-- axum/src/routing/url_params.rs | 7 +- axum/src/test_helpers/tracing_helpers.rs | 10 +- axum/src/util.rs | 8 +- 17 files changed, 258 insertions(+), 261 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4949916d1e..c9cbb26dcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ use_self = "warn" redundant_else = "warn" unnecessary_semicolon = "warn" manual_let_else = "warn" +option_if_let_else = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 56e9829022..83d4977694 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -26,11 +26,10 @@ where K: Send + 'static, { let mut k = Some(k); - if let Some(k) = ::downcast_mut::>(&mut k) { - Ok(k.take().unwrap()) - } else { - Err(k.unwrap()) - } + + ::downcast_mut::>(&mut k) + .and_then(Option::take) + .map_or_else(|| Err(k.unwrap()), Ok) } /// The body type used in axum requests and responses. diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 39d67a0c92..3119c66892 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -114,11 +114,7 @@ impl Multipart { .await .map_err(MultipartError::from_multer)?; - if let Some(field) = field { - Ok(Some(Field { inner: field })) - } else { - Ok(None) - } + field.map_or_else(|| Ok(None), |field| Ok(Some(Field { inner: field }))) } /// Convert the `Multipart` into a stream of its fields. diff --git a/axum-extra/src/response/attachment.rs b/axum-extra/src/response/attachment.rs index 2063d30f05..b442007506 100644 --- a/axum-extra/src/response/attachment.rs +++ b/axum-extra/src/response/attachment.rs @@ -55,12 +55,13 @@ impl Attachment { /// /// This updates the `Content-Disposition` header to add a filename. pub fn filename>(mut self, value: H) -> Self { - self.filename = if let Ok(filename) = value.try_into() { - Some(filename) - } else { - error!("Attachment filename contains invalid characters"); - None - }; + self.filename = value.try_into().map_or_else( + |_| { + error!("Attachment filename contains invalid characters"); + None + }, + Some + ); self } @@ -86,15 +87,17 @@ where headers.append(header::CONTENT_TYPE, content_type); } - let content_disposition = if let Some(filename) = self.filename { - let mut bytes = b"attachment; filename=\"".to_vec(); - bytes.extend_from_slice(filename.as_bytes()); - bytes.push(b'\"'); + let content_disposition = self.filename.map_or_else( + || HeaderValue::from_static("attachment"), + |filename| { + let mut bytes = b"attachment; filename=\"".to_vec(); + bytes.extend_from_slice(filename.as_bytes()); + bytes.push(b'\"'); - HeaderValue::from_bytes(&bytes).expect("This was a HeaderValue so this can not fail") - } else { - HeaderValue::from_static("attachment") - }; + HeaderValue::from_bytes(&bytes) + .expect("This was a HeaderValue so this can not fail") + }, + ); headers.append(header::CONTENT_DISPOSITION, content_disposition); diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index 8d0871d55b..b953236f1d 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -371,11 +371,10 @@ where .unwrap_or_else(|| Cow::Owned(format!("{path}/"))) }); - if let Some(new_uri) = new_uri { - Redirect::permanent(&new_uri.to_string()).into_response() - } else { - StatusCode::BAD_REQUEST.into_response() - } + new_uri.map_or_else( + || StatusCode::BAD_REQUEST.into_response(), + |new_uri| Redirect::permanent(&new_uri.to_string()).into_response(), + ) } if let Some(path_without_trailing_slash) = path.strip_suffix('/') { diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index a7f452b2e9..786feef611 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -52,20 +52,23 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn, kind: FunctionKind) -> TokenS let check_future_send = check_future_send(&item_fn, kind); - if let Some(check_input_order) = check_input_order(&item_fn, kind) { - quote! { - #check_input_order - #check_future_send - } - } else { - let check_inputs_impls_from_request = - check_inputs_impls_from_request(&item_fn, state_ty, kind); - - quote! { - #check_inputs_impls_from_request - #check_future_send - } - } + check_input_order(&item_fn, kind).map_or_else( + || { + let check_inputs_impls_from_request = + check_inputs_impls_from_request(&item_fn, state_ty, kind); + + quote! { + #check_inputs_impls_from_request + #check_future_send + } + }, + |check_input_order| { + quote! { + #check_input_order + #check_future_send + } + }, + ) }) } else { syn::Error::new_spanned( @@ -362,8 +365,9 @@ fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { Position::First(ty) => match extract_clean_typename(ty).as_deref() { Some("StatusCode" | "Response") => quote! {}, Some("Parts") => check_is_response_parts(ty, handler_ident, idx), - Some(_) | None => { - if let Some(tn) = well_known_last_response_type(ty) { + Some(_) | None => well_known_last_response_type(ty).map_or_else( + || check_into_response_parts(ty, handler_ident, idx), + |tn| { syn::Error::new_spanned( ty, format!( @@ -372,22 +376,19 @@ fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { ), ) .to_compile_error() - } else { - check_into_response_parts(ty, handler_ident, idx) - } - } + }, + ), }, - Position::Middle(ty) => { - if let Some(tn) = well_known_last_response_type(ty) { + Position::Middle(ty) => well_known_last_response_type(ty).map_or_else( + || check_into_response_parts(ty, handler_ident, idx), + |tn| { syn::Error::new_spanned( ty, format!("`{tn}` must be the last element in a response tuple"), ) .to_compile_error() - } else { - check_into_response_parts(ty, handler_ident, idx) - } - } + }, + ), Position::Last(ty) | Position::Only(ty) => check_into_response(handler_ident, ty), }) .collect::(), @@ -672,39 +673,42 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream { let name = format_ident!("__axum_macros_check_{}_into_response", item_fn.sig.ident); - if let Some(receiver) = self_receiver(item_fn) { - quote_spanned! {span=> - #make + self_receiver(item_fn).map_or_else( + || { + quote_spanned! {span=> + #[allow(warnings)] + #[allow(unreachable_code)] + #[doc(hidden)] + async fn #name() { + #make - #[allow(warnings)] - #[allow(unreachable_code)] - #[doc(hidden)] - async fn #name() { - fn check(_: T) + fn check(_: T) where T: ::axum::response::IntoResponse - {} - let value = #receiver #make_value_name().await; - check(value); - } - } - } else { - quote_spanned! {span=> - #[allow(warnings)] - #[allow(unreachable_code)] - #[doc(hidden)] - async fn #name() { - #make + {} - fn check(_: T) - where T: ::axum::response::IntoResponse - {} + let value = #make_value_name().await; - let value = #make_value_name().await; + check(value); + } + } + }, + |receiver| { + quote_spanned! {span=> + #make - check(value); + #[allow(warnings)] + #[allow(unreachable_code)] + #[doc(hidden)] + async fn #name() { + fn check(_: T) + where T: ::axum::response::IntoResponse + {} + let value = #receiver #make_value_name().await; + check(value); + } } - } - } + }, + ) } fn check_future_send(item_fn: &ItemFn, kind: FunctionKind) -> TokenStream { diff --git a/axum-macros/src/from_ref.rs b/axum-macros/src/from_ref.rs index 9cf317da97..99e543509c 100644 --- a/axum-macros/src/from_ref.rs +++ b/axum-macros/src/from_ref.rs @@ -39,19 +39,22 @@ fn expand_field(state: &Ident, idx: usize, field: &Field) -> TokenStream { let field_ty = &field.ty; let span = field.ty.span(); - let body = if let Some(field_ident) = &field.ident { - if matches!(field_ty, Type::Reference(_)) { - quote_spanned! {span=> state.#field_ident } - } else { - quote_spanned! {span=> state.#field_ident.clone() } - } - } else { - let idx = syn::Index { - index: idx as _, - span: field.span(), - }; - quote_spanned! {span=> state.#idx.clone() } - }; + let body = field.ident.as_ref().map_or_else( + || { + let idx = syn::Index { + index: idx as _, + span: field.span(), + }; + quote_spanned! {span=> state.#idx.clone() } + }, + |field_ident| { + if matches!(field_ty, Type::Reference(_)) { + quote_spanned! {span=> state.#field_ident } + } else { + quote_spanned! {span=> state.#field_ident.clone() } + } + }, + ); quote_spanned! {span=> #[allow(clippy::clone_on_copy, clippy::clone_on_ref_ptr)] diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index cc8bca7bb2..f20f5b7cd1 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -316,16 +316,14 @@ fn parse_single_generic_type_on_struct( } fn error_on_generic_ident(generic_ident: Option, tr: Trait) -> syn::Result<()> { - if let Some(generic_ident) = generic_ident { + generic_ident.map_or(Ok(()), |generic_ident| { Err(syn::Error::new_spanned( generic_ident, format_args!( "#[derive({tr})] only supports generics when used with #[from_request(via)]" ), )) - } else { - Ok(()) - } + }) } fn impl_struct_by_extracting_each_field( @@ -335,28 +333,30 @@ fn impl_struct_by_extracting_each_field( state: &State, tr: Trait, ) -> syn::Result { - let trait_fn_body = match state { - State::CannotInfer => quote! { + let trait_fn_body = if let State::CannotInfer = state { + quote! { ::std::unimplemented!() - }, - _ => { - let extract_fields = extract_fields(&fields, &rejection, tr)?; - quote! { - ::std::result::Result::Ok(Self { - #(#extract_fields)* - }) - } } - }; - - let rejection_ident = if let Some(rejection) = rejection { - quote!(#rejection) - } else if has_no_fields(&fields) { - quote!(::std::convert::Infallible) } else { - quote!(::axum::response::Response) + let extract_fields = extract_fields(&fields, &rejection, tr)?; + quote! { + ::std::result::Result::Ok(Self { + #(#extract_fields)* + }) + } }; + let rejection_ident = rejection.map_or_else( + || { + if has_no_fields(&fields) { + quote!(::std::convert::Infallible) + } else { + quote!(::axum::response::Response) + } + }, + |rejection| quote!(#rejection), + ); + let impl_generics = state .impl_generics() .collect::>(); @@ -417,16 +417,16 @@ fn extract_fields( tr: Trait, ) -> syn::Result> { fn member(field: &syn::Field, index: usize) -> TokenStream { - match &field.ident { - Some(ident) => quote! { #ident }, - _ => { + field.ident.as_ref().map_or_else( + || { let member = syn::Member::Unnamed(syn::Index { index: index as u32, span: field.span(), }); quote! { #member } - } - } + }, + |ident| quote! { #ident }, + ) } fn into_inner(via: &Option<(attr::kw::via, syn::Path)>, ty_span: Span) -> TokenStream { @@ -547,11 +547,7 @@ fn extract_fields( Ok(tokens) } else { let field_ty = into_outer(&via,ty_span,&field.ty); - let map_err = if let Some(rejection) = rejection { - quote! { <#rejection as ::std::convert::From<_>>::from } - } else { - quote! { ::axum::response::IntoResponse::into_response } - }; + let map_err = rejection.as_ref().map_or_else(|| quote! { ::axum::response::IntoResponse::into_response }, |rejection| quote! { <#rejection as ::std::convert::From<_>>::from }); let tokens = match tr { Trait::FromRequest => { @@ -619,11 +615,10 @@ fn extract_fields( } } else { let field_ty = into_outer(&via, ty_span, &field.ty); - let map_err = if let Some(rejection) = rejection { - quote! { <#rejection as ::std::convert::From<_>>::from } - } else { - quote! { ::axum::response::IntoResponse::into_response } - }; + let map_err = rejection.as_ref().map_or_else( + || quote! { ::axum::response::IntoResponse::into_response }, + |rejection| quote! { <#rejection as ::std::convert::From<_>>::from }, + ); quote_spanned! {ty_span=> #member: { @@ -728,17 +723,20 @@ fn impl_struct_by_extracting_all_at_once( let path_span = via_path.span(); - let (associated_rejection_type, map_err) = if let Some(rejection) = &rejection { - let rejection = quote! { #rejection }; - let map_err = quote! { ::std::convert::From::from }; - (rejection, map_err) - } else { - let rejection = quote! { - ::axum::response::Response - }; - let map_err = quote! { ::axum::response::IntoResponse::into_response }; - (rejection, map_err) - }; + let (associated_rejection_type, map_err) = rejection.as_ref().map_or_else( + || { + let rejection = quote! { + ::axum::response::Response + }; + let map_err = quote! { ::axum::response::IntoResponse::into_response }; + (rejection, map_err) + }, + |rejection| { + let rejection = quote! { #rejection }; + let map_err = quote! { ::std::convert::From::from }; + (rejection, map_err) + }, + ); // for something like // @@ -905,17 +903,20 @@ fn impl_enum_by_extracting_all_at_once( } } - let (associated_rejection_type, map_err) = if let Some(rejection) = &rejection { - let rejection = quote! { #rejection }; - let map_err = quote! { ::std::convert::From::from }; - (rejection, map_err) - } else { - let rejection = quote! { - ::axum::response::Response - }; - let map_err = quote! { ::axum::response::IntoResponse::into_response }; - (rejection, map_err) - }; + let (associated_rejection_type, map_err) = rejection.as_ref().map_or_else( + || { + let rejection = quote! { + ::axum::response::Response + }; + let map_err = quote! { ::axum::response::IntoResponse::into_response }; + (rejection, map_err) + }, + |rejection| { + let rejection = quote! { #rejection }; + let map_err = quote! { ::std::convert::From::from }; + (rejection, map_err) + }, + ); let path_span = path.span(); @@ -1036,11 +1037,9 @@ fn infer_state_type_from_field_attributes(fields: &Fields) -> impl Iterator bool { - if let Some(last_segment) = path.segments.last() { - last_segment.ident == "State" - } else { - false - } + path.segments + .last() + .is_some_and(|last_segment| last_segment.ident == "State") } fn state_from_via(ident: &Ident, via: &Path) -> Option { diff --git a/axum-macros/src/typed_path.rs b/axum-macros/src/typed_path.rs index 397cc94c0d..85079693b2 100644 --- a/axum-macros/src/typed_path.rs +++ b/axum-macros/src/typed_path.rs @@ -304,20 +304,22 @@ fn expand_unit_fields( } }; - let rejection_assoc_type = if let Some(rejection) = &rejection { - quote! { #rejection } - } else { - quote! { ::axum::http::StatusCode } - }; - let create_rejection = if let Some(rejection) = &rejection { - quote! { - Err(<#rejection as ::std::default::Default>::default()) - } - } else { - quote! { - Err(::axum::http::StatusCode::NOT_FOUND) - } - }; + let rejection_assoc_type = rejection.as_ref().map_or_else( + || quote! { ::axum::http::StatusCode }, + |rejection| quote! { #rejection }, + ); + let create_rejection = rejection.as_ref().map_or_else( + || { + quote! { + Err(::axum::http::StatusCode::NOT_FOUND) + } + }, + |rejection| { + quote! { + Err(<#rejection as ::std::default::Default>::default()) + } + }, + ); let from_request_impl = quote! { #[automatically_derived] @@ -382,18 +384,17 @@ fn parse_path(path: &LitStr) -> syn::Result> { path.value() .split('/') .map(|segment| { - if let Some(capture) = segment + segment .strip_prefix('{') .and_then(|segment| segment.strip_suffix('}')) .and_then(|segment| { (!segment.starts_with('{') && !segment.ends_with('}')).then_some(segment) }) .map(|capture| capture.strip_prefix('*').unwrap_or(capture)) - { - Ok(Segment::Capture(capture.to_owned(), path.span())) - } else { - Ok(Segment::Static(segment.to_owned())) - } + .map_or_else( + || Ok(Segment::Static(segment.to_owned())), + |capture| Ok(Segment::Capture(capture.to_owned(), path.span())), + ) }) .collect() } @@ -410,10 +411,9 @@ fn path_rejection() -> TokenStream { } fn rejection_assoc_type(rejection: &Option) -> TokenStream { - match rejection { - Some(rejection) => quote! { #rejection }, - None => path_rejection(), - } + rejection + .as_ref() + .map_or_else(path_rejection, |rejection| quote! { #rejection }) } fn map_err_rejection(rejection: &Option) -> TokenStream { diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index eb6ef49929..3f9a03801a 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -123,20 +123,21 @@ pub(crate) fn set_matched_path_for_request( // a previous `MatchedPath` might exist if we're inside a nested Router fn append_nested_matched_path(matched_path: &Arc, extensions: &http::Extensions) -> Arc { - if let Some(previous) = extensions + extensions .get::() .map(|matched_path| matched_path.as_str()) .or_else(|| Some(&extensions.get::()?.0)) - { - let previous = previous - .strip_suffix(NEST_TAIL_PARAM_CAPTURE) - .unwrap_or(previous); - - let matched_path = format!("{previous}{matched_path}"); - matched_path.into() - } else { - Arc::clone(matched_path) - } + .map_or_else( + || Arc::clone(matched_path), + |previous| { + let previous = previous + .strip_suffix(NEST_TAIL_PARAM_CAPTURE) + .unwrap_or(previous); + + let matched_path = format!("{previous}{matched_path}"); + matched_path.into() + }, + ) } #[cfg(test)] diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index e9832e8e84..771b506f12 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -112,14 +112,15 @@ impl Multipart { .await .map_err(MultipartError::from_multer)?; - if let Some(field) = field { - Ok(Some(Field { - inner: field, - _multipart: self, - })) - } else { - Ok(None) - } + field.map_or_else( + || Ok(None), + |field| { + Ok(Some(Field { + inner: field, + _multipart: self, + })) + }, + ) } } diff --git a/axum/src/extract/nested_path.rs b/axum/src/extract/nested_path.rs index 9b3eba5641..14c87e6c4f 100644 --- a/axum/src/extract/nested_path.rs +++ b/axum/src/extract/nested_path.rs @@ -54,10 +54,12 @@ where type Rejection = NestedPathRejection; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - match parts.extensions.get::() { - Some(nested_path) => Ok(nested_path.clone()), - None => Err(NestedPathRejection), - } + parts + .extensions + .get::() + .map_or(Err(NestedPathRejection), |nested_path| { + Ok(nested_path.clone()) + }) } } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 60e5c4b308..8e3ec81bc9 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -346,30 +346,28 @@ impl WebSocketUpgrade { callback(socket).await; }); - let mut response = if let Some(sec_websocket_key) = &self.sec_websocket_key { - // If `sec_websocket_key` was `Some`, we are using HTTP/1.1. - - #[allow(clippy::declare_interior_mutable_const)] - const UPGRADE: HeaderValue = HeaderValue::from_static("upgrade"); - #[allow(clippy::declare_interior_mutable_const)] - const WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket"); - - Response::builder() - .status(StatusCode::SWITCHING_PROTOCOLS) - .header(header::CONNECTION, UPGRADE) - .header(header::UPGRADE, WEBSOCKET) - .header( - header::SEC_WEBSOCKET_ACCEPT, - sign(sec_websocket_key.as_bytes()), - ) - .body(Body::empty()) - .unwrap() - } else { - // Otherwise, we are HTTP/2+. As established in RFC 9113 section 8.5, we just respond - // with a 2XX with an empty body: - // . - Response::new(Body::empty()) - }; + let mut response = self.sec_websocket_key.as_ref().map_or_else( + || Response::new(Body::empty()), + |sec_websocket_key| { + // If `sec_websocket_key` was `Some`, we are using HTTP/1.1. + + #[allow(clippy::declare_interior_mutable_const)] + const UPGRADE: HeaderValue = HeaderValue::from_static("upgrade"); + #[allow(clippy::declare_interior_mutable_const)] + const WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket"); + + Response::builder() + .status(StatusCode::SWITCHING_PROTOCOLS) + .header(header::CONNECTION, UPGRADE) + .header(header::UPGRADE, WEBSOCKET) + .header( + header::SEC_WEBSOCKET_ACCEPT, + sign(sec_websocket_key.as_bytes()), + ) + .body(Body::empty()) + .unwrap() + }, + ); if let Some(protocol) = self.protocol { response @@ -479,11 +477,9 @@ where } fn header_eq(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - if let Some(header) = headers.get(&key) { + headers.get(&key).is_some_and(|header| { header.as_bytes().eq_ignore_ascii_case(value.as_bytes()) - } else { - false - } + }) } fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { @@ -491,11 +487,8 @@ fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> return false; }; - if let Ok(header) = std::str::from_utf8(header.as_bytes()) { - header.to_ascii_lowercase().contains(value) - } else { - false - } + std::str::from_utf8(header.as_bytes()) + .is_ok_and(|header| header.to_ascii_lowercase().contains(value)) } /// A stream of WebSocket messages. diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 4a39542bd1..9fa73c664a 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1049,20 +1049,21 @@ where match (first, second) { (MethodEndpoint::None, MethodEndpoint::None) => Ok(MethodEndpoint::None), (pick, MethodEndpoint::None) | (MethodEndpoint::None, pick) => Ok(pick), - _ => { - if let Some(path) = path { + _ => path.map_or_else( + || { Err(format!( - "Overlapping method route. Handler for `{name} {path}` already exists" + "Overlapping method route. Cannot merge two method routes that both \ + define `{name}`" ) .into()) - } else { + }, + |path| { Err(format!( - "Overlapping method route. Cannot merge two method routes that both \ - define `{name}`" + "Overlapping method route. Handler for `{name} {path}` already exists" ) .into()) - } - } + }, + ), } } diff --git a/axum/src/routing/url_params.rs b/axum/src/routing/url_params.rs index 1649a9e4cf..467ef0ca0f 100644 --- a/axum/src/routing/url_params.rs +++ b/axum/src/routing/url_params.rs @@ -22,11 +22,8 @@ pub(super) fn insert_url_params(extensions: &mut Extensions, params: Params<'_, .filter(|(key, _)| !key.starts_with(super::NEST_TAIL_PARAM)) .filter(|(key, _)| !key.starts_with(super::FALLBACK_PARAM)) .map(|(k, v)| { - if let Some(decoded) = PercentDecodedStr::new(v) { - Ok((Arc::from(k), decoded)) - } else { - Err(Arc::from(k)) - } + PercentDecodedStr::new(v) + .map_or_else(|| Err(Arc::from(k)), |decoded| Ok((Arc::from(k), decoded))) }) .collect::, _>>(); diff --git a/axum/src/test_helpers/tracing_helpers.rs b/axum/src/test_helpers/tracing_helpers.rs index adf4331fd0..93f7efee26 100644 --- a/axum/src/test_helpers/tracing_helpers.rs +++ b/axum/src/test_helpers/tracing_helpers.rs @@ -114,14 +114,14 @@ struct Writer<'a>(&'a TestMakeWriter); impl io::Write for Writer<'_> { fn write(&mut self, buf: &[u8]) -> io::Result { - match &mut *self.0.write.lock().unwrap() { - Some(vec) => { + (*self.0.write.lock().unwrap()).as_mut().map_or_else( + || Err(io::Error::other("inner writer has been taken")), + |vec| { let len = buf.len(); vec.extend(buf); Ok(len) - } - None => Err(io::Error::other("inner writer has been taken")), - } + }, + ) } fn flush(&mut self) -> io::Result<()> { diff --git a/axum/src/util.rs b/axum/src/util.rs index e4014c5916..d6fa75079f 100644 --- a/axum/src/util.rs +++ b/axum/src/util.rs @@ -102,11 +102,9 @@ where K: Send + 'static, { let mut k = Some(k); - if let Some(k) = ::downcast_mut::>(&mut k) { - Ok(k.take().unwrap()) - } else { - Err(k.unwrap()) - } + ::downcast_mut::>(&mut k) + .and_then(Option::take) + .map_or_else(|| Err(k.unwrap()), Ok) } #[test] From 5fa5f5cd810077952e05dba49a04be907750d264 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:54:12 +0200 Subject: [PATCH 08/19] perf: mark elligible functions/methods as `const` Some functions and methods aren't marked as `const`, even though they could be. Not marking them as `const` prevents callers of the function/method from being `const` as well. This fixes the `missing_const_for_fn` clippy lint. I also marked some methods and functions as `const` that clippy didn't find. NOTE: Removing the `const` marker again from public functions/methods in the future will break API compatibility. NOTE-2: I marked some of the `status` methods as const. I don't know how axum's internals work very well, so it's possible that I may have broken something, but all tests are passing sooo... --- Cargo.toml | 1 + axum-core/src/body.rs | 2 +- axum-core/src/macros.rs | 6 +++--- axum-core/src/response/into_response_parts.rs | 4 ++-- axum-extra/src/extract/multipart.rs | 2 +- axum-extra/src/json_lines.rs | 2 +- axum-extra/src/response/attachment.rs | 2 +- axum-extra/src/response/file_stream.rs | 4 ++-- axum-extra/src/response/multiple.rs | 2 +- axum-extra/src/typed_header.rs | 8 ++++---- axum-macros/src/debug_handler.rs | 2 +- axum-macros/src/from_request/mod.rs | 2 +- axum/benches/benches.rs | 4 ++-- axum/src/extract/multipart.rs | 2 +- axum/src/extract/path/de.rs | 4 ++-- axum/src/extract/path/mod.rs | 16 ++++++++-------- axum/src/extract/ws.rs | 16 ++++++++-------- axum/src/handler/service.rs | 4 ++-- axum/src/macros.rs | 2 +- axum/src/response/redirect.rs | 2 +- axum/src/response/sse.rs | 8 ++++---- axum/src/routing/into_make_service.rs | 2 +- axum/src/routing/method_filter.rs | 2 +- axum/src/routing/method_routing.rs | 4 ++-- axum/src/routing/mod.rs | 2 +- axum/src/routing/route.rs | 6 +++--- axum/src/serve/mod.rs | 4 ++-- axum/src/test_helpers/mod.rs | 4 ++-- axum/src/test_helpers/test_client.rs | 2 +- axum/src/util.rs | 2 +- 30 files changed, 62 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9cbb26dcf..70a32319ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ redundant_else = "warn" unnecessary_semicolon = "warn" manual_let_else = "warn" option_if_let_else = "warn" +missing_const_for_fn = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 83d4977694..2566b48559 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -73,7 +73,7 @@ impl Body { /// /// [`http_body_util::BodyStream`]: https://docs.rs/http-body-util/latest/http_body_util/struct.BodyStream.html #[must_use] - pub fn into_data_stream(self) -> BodyDataStream { + pub const fn into_data_stream(self) -> BodyDataStream { BodyDataStream { inner: self } } } diff --git a/axum-core/src/macros.rs b/axum-core/src/macros.rs index 6bc24c3027..b21b0203e3 100644 --- a/axum-core/src/macros.rs +++ b/axum-core/src/macros.rs @@ -54,7 +54,7 @@ macro_rules! __define_rejection { } /// Get the status code used for this rejection. - pub fn status(&self) -> http::StatusCode { + pub const fn status(&self) -> http::StatusCode { http::StatusCode::$status } } @@ -113,7 +113,7 @@ macro_rules! __define_rejection { /// Get the status code used for this rejection. #[must_use] - pub fn status(&self) -> http::StatusCode { + pub const fn status(&self) -> http::StatusCode { http::StatusCode::$status } } @@ -192,7 +192,7 @@ macro_rules! __composite_rejection { /// Get the status code used for this rejection. #[must_use] - pub fn status(&self) -> http::StatusCode { + pub const fn status(&self) -> http::StatusCode { match self { $( Self::$variant(inner) => inner.status(), diff --git a/axum-core/src/response/into_response_parts.rs b/axum-core/src/response/into_response_parts.rs index 955648238d..5fb56acbe1 100644 --- a/axum-core/src/response/into_response_parts.rs +++ b/axum-core/src/response/into_response_parts.rs @@ -165,13 +165,13 @@ pub struct TryIntoHeaderError { } impl TryIntoHeaderError { - pub(super) fn key(err: K) -> Self { + pub(super) const fn key(err: K) -> Self { Self { kind: TryIntoHeaderErrorKind::Key(err), } } - pub(super) fn value(err: V) -> Self { + pub(super) const fn value(err: V) -> Self { Self { kind: TryIntoHeaderErrorKind::Value(err), } diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 3119c66892..f2f9892b7a 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -237,7 +237,7 @@ pub struct MultipartError { } impl MultipartError { - fn from_multer(multer: multer::Error) -> Self { + const fn from_multer(multer: multer::Error) -> Self { Self { source: multer } } diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index 9aea94dc83..638ce53228 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -91,7 +91,7 @@ pub struct AsResponse; impl JsonLines { /// Create a new `JsonLines` from a stream of items. - pub fn new(stream: S) -> Self { + pub const fn new(stream: S) -> Self { Self { inner: Inner::Response { stream }, _marker: PhantomData, diff --git a/axum-extra/src/response/attachment.rs b/axum-extra/src/response/attachment.rs index b442007506..cab08cf292 100644 --- a/axum-extra/src/response/attachment.rs +++ b/axum-extra/src/response/attachment.rs @@ -43,7 +43,7 @@ pub struct Attachment { impl Attachment { /// Creates a new [`Attachment`]. - pub fn new(inner: T) -> Self { + pub const fn new(inner: T) -> Self { Self { inner, filename: None, diff --git a/axum-extra/src/response/file_stream.rs b/axum-extra/src/response/file_stream.rs index ed1afdff65..5df0977bfc 100644 --- a/axum-extra/src/response/file_stream.rs +++ b/axum-extra/src/response/file_stream.rs @@ -61,7 +61,7 @@ where S::Error: Into, { /// Create a new [`FileStream`] - pub fn new(stream: S) -> Self { + pub const fn new(stream: S) -> Self { Self { stream, file_name: None, @@ -124,7 +124,7 @@ where } /// Set the size of the file. - pub fn content_size(mut self, len: u64) -> Self { + pub const fn content_size(mut self, len: u64) -> Self { self.content_size = Some(len); self } diff --git a/axum-extra/src/response/multiple.rs b/axum-extra/src/response/multiple.rs index 61bc1c75a9..aece900909 100644 --- a/axum-extra/src/response/multiple.rs +++ b/axum-extra/src/response/multiple.rs @@ -25,7 +25,7 @@ impl MultipartForm { /// let form = MultipartForm::with_parts(parts); /// ``` #[must_use] - pub fn with_parts(parts: Vec) -> Self { + pub const fn with_parts(parts: Vec) -> Self { Self { parts } } } diff --git a/axum-extra/src/typed_header.rs b/axum-extra/src/typed_header.rs index 41cf91819e..0844f4a72a 100644 --- a/axum-extra/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -138,13 +138,13 @@ pub struct TypedHeaderRejection { impl TypedHeaderRejection { /// Name of the header that caused the rejection #[must_use] - pub fn name(&self) -> &http::header::HeaderName { + pub const fn name(&self) -> &http::header::HeaderName { self.name } /// Reason why the header extraction has failed #[must_use] - pub fn reason(&self) -> &TypedHeaderRejectionReason { + pub const fn reason(&self) -> &TypedHeaderRejectionReason { &self.reason } @@ -152,7 +152,7 @@ impl TypedHeaderRejection { /// /// [`Missing`]: TypedHeaderRejectionReason::Missing #[must_use] - pub fn is_missing(&self) -> bool { + pub const fn is_missing(&self) -> bool { self.reason.is_missing() } } @@ -173,7 +173,7 @@ impl TypedHeaderRejectionReason { /// /// [`Missing`]: TypedHeaderRejectionReason::Missing #[must_use] - pub fn is_missing(&self) -> bool { + pub const fn is_missing(&self) -> bool { matches!(self, Self::Missing) } } diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 786feef611..e2b28fc5ac 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -107,7 +107,7 @@ impl fmt::Display for FunctionKind { } impl FunctionKind { - fn name_uppercase_plural(&self) -> &'static str { + const fn name_uppercase_plural(&self) -> &'static str { match self { Self::Handler => "Handlers", Self::Middleware => "Middleware", diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index f20f5b7cd1..e8b093e4dd 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -1061,4 +1061,4 @@ fn ui() { /// } /// ``` #[allow(dead_code)] -fn test_field_doesnt_impl_from_request() {} +const fn test_field_doesnt_impl_from_request() {} diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index c38ef91830..dd82a221b4 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -102,7 +102,7 @@ struct Payload { b: bool, } -fn benchmark(name: &'static str) -> BenchmarkBuilder { +const fn benchmark(name: &'static str) -> BenchmarkBuilder { BenchmarkBuilder { name, path: None, @@ -122,7 +122,7 @@ struct BenchmarkBuilder { macro_rules! config_method { ($name:ident, $ty:ty) => { - fn $name(mut self, $name: $ty) -> Self { + const fn $name(mut self, $name: $ty) -> Self { self.$name = Some($name); self } diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 771b506f12..2e5db67f5c 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -238,7 +238,7 @@ pub struct MultipartError { } impl MultipartError { - fn from_multer(multer: multer::Error) -> Self { + const fn from_multer(multer: multer::Error) -> Self { Self { source: multer } } diff --git a/axum/src/extract/path/de.rs b/axum/src/extract/path/de.rs index 32883f1dca..f171717b88 100644 --- a/axum/src/extract/path/de.rs +++ b/axum/src/extract/path/de.rs @@ -48,7 +48,7 @@ pub(crate) struct PathDeserializer<'de> { impl<'de> PathDeserializer<'de> { #[inline] - pub(crate) fn new(url_params: &'de [(Arc, PercentDecodedStr)]) -> Self { + pub(crate) const fn new(url_params: &'de [(Arc, PercentDecodedStr)]) -> Self { PathDeserializer { url_params } } } @@ -637,7 +637,7 @@ enum KeyOrIdx<'de> { } impl<'de> KeyOrIdx<'de> { - fn key(&self) -> &'de str { + const fn key(&self) -> &'de str { match &self { Self::Key(key) => key, Self::Idx { key, .. } => key, diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 0661dfd1c8..2514e52866 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -178,7 +178,7 @@ where } } - fn failed_to_deserialize_path_params(err: PathDeserializationError) -> PathRejection { + const fn failed_to_deserialize_path_params(err: PathDeserializationError) -> PathRejection { PathRejection::FailedToDeserializePathParams(FailedToDeserializePathParams(err)) } @@ -220,16 +220,16 @@ pub(crate) struct PathDeserializationError { } impl PathDeserializationError { - pub(super) fn new(kind: ErrorKind) -> Self { + pub(super) const fn new(kind: ErrorKind) -> Self { Self { kind } } - pub(super) fn wrong_number_of_parameters() -> WrongNumberOfParameters<()> { + pub(super) const fn wrong_number_of_parameters() -> WrongNumberOfParameters<()> { WrongNumberOfParameters { got: () } } #[track_caller] - pub(super) fn unsupported_type(name: &'static str) -> Self { + pub(super) const fn unsupported_type(name: &'static str) -> Self { Self::new(ErrorKind::UnsupportedType { name }) } } @@ -246,7 +246,7 @@ impl WrongNumberOfParameters { } impl WrongNumberOfParameters { - pub(super) fn expected(self, expected: usize) -> PathDeserializationError { + pub(super) const fn expected(self, expected: usize) -> PathDeserializationError { PathDeserializationError::new(ErrorKind::WrongNumberOfParameters { got: self.got, expected, @@ -408,7 +408,7 @@ pub struct FailedToDeserializePathParams(PathDeserializationError); impl FailedToDeserializePathParams { /// Get a reference to the underlying error kind. #[must_use] - pub fn kind(&self) -> &ErrorKind { + pub const fn kind(&self) -> &ErrorKind { &self.0.kind } @@ -436,7 +436,7 @@ impl FailedToDeserializePathParams { /// Get the status code used for this rejection. #[must_use] - pub fn status(&self) -> StatusCode { + pub const fn status(&self) -> StatusCode { match self.0.kind { ErrorKind::Message(_) | ErrorKind::DeserializeError { .. } @@ -573,7 +573,7 @@ impl InvalidUtf8InPathParam { /// Get the status code used for this rejection. #[must_use] - pub fn status(&self) -> StatusCode { + pub const fn status(&self) -> StatusCode { StatusCode::BAD_REQUEST } } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 8e3ec81bc9..fa6cfde845 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -152,7 +152,7 @@ impl std::fmt::Debug for WebSocketUpgrade { impl WebSocketUpgrade { /// Read buffer capacity. The default value is 128KiB - pub fn read_buffer_size(mut self, size: usize) -> Self { + pub const fn read_buffer_size(mut self, size: usize) -> Self { self.config.read_buffer_size = size; self } @@ -166,7 +166,7 @@ impl WebSocketUpgrade { /// It is often more optimal to allow them to buffer a little, hence the default value. /// /// Note: [`flush`](SinkExt::flush) will always fully write the buffer regardless. - pub fn write_buffer_size(mut self, size: usize) -> Self { + pub const fn write_buffer_size(mut self, size: usize) -> Self { self.config.write_buffer_size = size; self } @@ -182,25 +182,25 @@ impl WebSocketUpgrade { /// /// Note: Should always be at least [`write_buffer_size + 1 message`](Self::write_buffer_size) /// and probably a little more depending on error handling strategy. - pub fn max_write_buffer_size(mut self, max: usize) -> Self { + pub const fn max_write_buffer_size(mut self, max: usize) -> Self { self.config.max_write_buffer_size = max; self } /// Set the maximum message size (defaults to 64 megabytes) - pub fn max_message_size(mut self, max: usize) -> Self { + pub const fn max_message_size(mut self, max: usize) -> Self { self.config.max_message_size = Some(max); self } /// Set the maximum frame size (defaults to 16 megabytes) - pub fn max_frame_size(mut self, max: usize) -> Self { + pub const fn max_frame_size(mut self, max: usize) -> Self { self.config.max_frame_size = Some(max); self } /// Allow server to accept unmasked frames (defaults to false) - pub fn accept_unmasked_frames(mut self, accept: bool) -> Self { + pub const fn accept_unmasked_frames(mut self, accept: bool) -> Self { self.config.accept_unmasked_frames = accept; self } @@ -269,7 +269,7 @@ impl WebSocketUpgrade { /// If [`protocols()`][Self::protocols] has been called and a matching /// protocol has been selected, the return value will be `Some` containing /// said protocol. Otherwise, it will be `None`. - pub fn selected_protocol(&self) -> Option<&HeaderValue> { + pub const fn selected_protocol(&self) -> Option<&HeaderValue> { self.protocol.as_ref() } @@ -517,7 +517,7 @@ impl WebSocket { } /// Return the selected WebSocket subprotocol, if one has been chosen. - pub fn protocol(&self) -> Option<&HeaderValue> { + pub const fn protocol(&self) -> Option<&HeaderValue> { self.protocol.as_ref() } } diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 1bfdeb4ecb..b026e67f81 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -27,7 +27,7 @@ pub struct HandlerService { impl HandlerService { /// Get a reference to the state. - pub fn state(&self) -> &S { + pub const fn state(&self) -> &S { &self.state } @@ -60,7 +60,7 @@ impl HandlerService { /// ``` /// /// [`MakeService`]: tower::make::MakeService - pub fn into_make_service(self) -> IntoMakeService { + pub const fn into_make_service(self) -> IntoMakeService { IntoMakeService::new(self) } diff --git a/axum/src/macros.rs b/axum/src/macros.rs index 37b8fc3b26..09370b863e 100644 --- a/axum/src/macros.rs +++ b/axum/src/macros.rs @@ -17,7 +17,7 @@ macro_rules! opaque_future { } impl<$($param),*> $name<$($param),*> { - pub(crate) fn new(future: $actual) -> Self { + pub(crate) const fn new(future: $actual) -> Self { Self { future } } } diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index e33928cd26..086c6ef78a 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -57,7 +57,7 @@ impl Redirect { /// Returns the HTTP status code of the `Redirect`. #[must_use] - pub fn status_code(&self) -> StatusCode { + pub const fn status_code(&self) -> StatusCode { self.status_code } diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index d519b0d42f..823234e53c 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -58,7 +58,7 @@ impl Sse { /// [`Event`]s. /// /// See the [module docs](self) for more details. - pub fn new(stream: S) -> Self + pub const fn new(stream: S) -> Self where S: TryStream + Send + 'static, S::Error: Into, @@ -453,7 +453,7 @@ pub struct KeepAlive { impl KeepAlive { /// Create a new `KeepAlive`. - pub fn new() -> Self { + pub const fn new() -> Self { Self { event: Event::DEFAULT_KEEP_ALIVE, max_interval: Duration::from_secs(15), @@ -463,7 +463,7 @@ impl KeepAlive { /// Customize the interval between keep-alive messages. /// /// Default is 15 seconds. - pub fn interval(mut self, time: Duration) -> Self { + pub const fn interval(mut self, time: Duration) -> Self { self.max_interval = time; self } @@ -566,7 +566,7 @@ where } } -fn memchr_split(needle: u8, haystack: &[u8]) -> MemchrSplit<'_> { +const fn memchr_split(needle: u8, haystack: &[u8]) -> MemchrSplit<'_> { MemchrSplit { needle, haystack: Some(haystack), diff --git a/axum/src/routing/into_make_service.rs b/axum/src/routing/into_make_service.rs index 36da73a21e..73d002e7bf 100644 --- a/axum/src/routing/into_make_service.rs +++ b/axum/src/routing/into_make_service.rs @@ -14,7 +14,7 @@ pub struct IntoMakeService { } impl IntoMakeService { - pub(crate) fn new(svc: S) -> Self { + pub(crate) const fn new(svc: S) -> Self { Self { svc } } } diff --git a/axum/src/routing/method_filter.rs b/axum/src/routing/method_filter.rs index 4b3d142356..2cb2b7e1a6 100644 --- a/axum/src/routing/method_filter.rs +++ b/axum/src/routing/method_filter.rs @@ -72,7 +72,7 @@ pub struct NoMatchingMethodFilter { impl NoMatchingMethodFilter { /// Get the [`Method`] that couldn't be converted to a [`MethodFilter`]. - pub fn method(&self) -> &Method { + pub const fn method(&self) -> &Method { &self.method } } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 9fa73c664a..db089e0dee 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1233,11 +1233,11 @@ impl MethodEndpoint where S: Clone, { - fn is_some(&self) -> bool { + const fn is_some(&self) -> bool { matches!(self, Self::Route(_) | Self::BoxedHandler(_)) } - fn is_none(&self) -> bool { + const fn is_none(&self) -> bool { matches!(self, Self::None) } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 416b4ef480..b83fa5138f 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -515,7 +515,7 @@ where /// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See /// that method for more details. #[must_use] - pub fn into_service(self) -> RouterIntoService { + pub const fn into_service(self) -> RouterIntoService { RouterIntoService { router: self, _marker: PhantomData, diff --git a/axum/src/routing/route.rs b/axum/src/routing/route.rs index 6730078787..adfa989aa4 100644 --- a/axum/src/routing/route.rs +++ b/axum/src/routing/route.rs @@ -117,7 +117,7 @@ pin_project! { } impl RouteFuture { - fn new( + const fn new( method: Method, inner: Oneshot, Request>, ) -> Self { @@ -134,7 +134,7 @@ impl RouteFuture { self } - pub(crate) fn not_top_level(mut self) -> Self { + pub(crate) const fn not_top_level(mut self) -> Self { self.top_level = false; self } @@ -216,7 +216,7 @@ pin_project! { } impl InfallibleRouteFuture { - pub(crate) fn new(future: RouteFuture) -> Self { + pub(crate) const fn new(future: RouteFuture) -> Self { Self { future } } } diff --git a/axum/src/serve/mod.rs b/axum/src/serve/mod.rs index 3a470af2a7..b6eee7bd06 100644 --- a/axum/src/serve/mod.rs +++ b/axum/src/serve/mod.rs @@ -96,7 +96,7 @@ pub use self::listener::{Listener, ListenerExt, TapIo}; /// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info /// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -pub fn serve(listener: L, make_service: M) -> Serve +pub const fn serve(listener: L, make_service: M) -> Serve where L: Listener, M: for<'a> Service, Error = Infallible, Response = S>, @@ -459,7 +459,7 @@ where } /// Returns the remote address that this stream is bound to. - pub fn remote_addr(&self) -> &L::Addr { + pub const fn remote_addr(&self) -> &L::Addr { &self.remote_addr } } diff --git a/axum/src/test_helpers/mod.rs b/axum/src/test_helpers/mod.rs index 06f9101c3f..3f03caa084 100644 --- a/axum/src/test_helpers/mod.rs +++ b/axum/src/test_helpers/mod.rs @@ -12,9 +12,9 @@ pub(crate) mod tracing_helpers; pub(crate) mod counting_cloneable_state; #[cfg(test)] -pub(crate) fn assert_send() {} +pub(crate) const fn assert_send() {} #[cfg(test)] -pub(crate) fn assert_sync() {} +pub(crate) const fn assert_sync() {} #[allow(dead_code)] pub(crate) struct NotSendSync(*const ()); diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 408960e186..5fc4cf5a4f 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -89,7 +89,7 @@ impl TestClient { #[allow(dead_code)] #[must_use] - pub fn server_port(&self) -> u16 { + pub const fn server_port(&self) -> u16 { self.addr.port() } } diff --git a/axum/src/util.rs b/axum/src/util.rs index d6fa75079f..8ab3d53230 100644 --- a/axum/src/util.rs +++ b/axum/src/util.rs @@ -51,7 +51,7 @@ pub(crate) struct MapIntoResponse { } impl MapIntoResponse { - pub(crate) fn new(inner: S) -> Self { + pub(crate) const fn new(inner: S) -> Self { Self { inner } } } From 869e21980566715a94aebc928628e942bd5db23a Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 16:15:54 +0200 Subject: [PATCH 09/19] style: use `let..else` for router downcast Seems to not have been found by clippy on my machine. This fixes the `manual_let_else` clippy lint. See-also: ddf5830e8450c5c0d909b5c2b97e42c4b1d7313a --- axum/src/routing/mod.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index b83fa5138f..7a0b5cf1c9 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -187,14 +187,11 @@ where T::Response: IntoResponse, T::Future: Send + 'static, { - let service = match try_downcast::(service) { - Ok(_) => { - panic!( - "Invalid route: `Router::route_service` cannot be used with `Router`s. \ - Use `Router::nest` instead" - ); - } - Err(service) => service, + let Err(service) = try_downcast::(service) else { + panic!( + "Invalid route: `Router::route_service` cannot be used with `Router`s. \ + Use `Router::nest` instead" + ); }; tap_inner!(self, mut this => { From 433b705000895f6ab3c7b66830fde83b42ac27ae Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 16:18:16 +0200 Subject: [PATCH 10/19] style(fmt): run `cargo fmt` Forgot to run `cargo-fmt` during a pervious commit. See-also: d8b6ef2e09ebaa226e5edf9f59a9ce015f507ce3 --- axum-extra/src/response/attachment.rs | 2 +- axum/src/extract/ws.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/axum-extra/src/response/attachment.rs b/axum-extra/src/response/attachment.rs index cab08cf292..64f647d1e2 100644 --- a/axum-extra/src/response/attachment.rs +++ b/axum-extra/src/response/attachment.rs @@ -60,7 +60,7 @@ impl Attachment { error!("Attachment filename contains invalid characters"); None }, - Some + Some, ); self } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index fa6cfde845..99c582c778 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -477,9 +477,9 @@ where } fn header_eq(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - headers.get(&key).is_some_and(|header| { - header.as_bytes().eq_ignore_ascii_case(value.as_bytes()) - }) + headers + .get(&key) + .is_some_and(|header| header.as_bytes().eq_ignore_ascii_case(value.as_bytes())) } fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { From d8389ac96be6aa862546952c39b9420ae720f846 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 16:46:36 +0200 Subject: [PATCH 11/19] style: remove unnecessary `if` negation `if !..else` is a confusing syntax, as it does the inverse of what the rest of the lines says. By reordering the `if` statement we can make the `if` statement more clear. This fixes the `if_not_else` clippy lint. --- Cargo.toml | 1 + axum-macros/src/with_position.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70a32319ec..eb7c56b017 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ unnecessary_semicolon = "warn" manual_let_else = "warn" option_if_let_else = "warn" missing_const_for_fn = "warn" +if_not_else = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-macros/src/with_position.rs b/axum-macros/src/with_position.rs index b5c1a60922..770efa5eaa 100644 --- a/axum-macros/src/with_position.rs +++ b/axum-macros/src/with_position.rs @@ -83,7 +83,14 @@ impl Iterator for WithPosition { fn next(&mut self) -> Option { match self.peekable.next() { Some(item) => { - if !self.handled_first { + if self.handled_first { + // Have seen the first item, and there's something left. + // Peek to see if this is the last item. + match self.peekable.peek() { + Some(_) => Some(Position::Middle(item)), + None => Some(Position::Last(item)), + } + } else { // Haven't seen the first item yet, and there is one to give. self.handled_first = true; // Peek to see if this is also the last item, @@ -92,13 +99,6 @@ impl Iterator for WithPosition { Some(_) => Some(Position::First(item)), None => Some(Position::Only(item)), } - } else { - // Have seen the first item, and there's something left. - // Peek to see if this is the last item. - match self.peekable.peek() { - Some(_) => Some(Position::Middle(item)), - None => Some(Position::Last(item)), - } } } // Iterator is finished. From f61e16f7b012cced8a2df562accc8c0035bcabcf Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 16:55:59 +0200 Subject: [PATCH 12/19] style: use dedicated `map_or*` methods The `.map(_).unwrap_or*(_)` methods are inconcise and hard-to-read. This commit replaces them with the dedicated `map_or*` methods instead. This fixes the `map_unwrap_or` clippy lint. NOTE: This makes it harder to search through the entire codebase for `unwrap`s. --- Cargo.toml | 1 + axum-extra/src/extract/query.rs | 3 +-- axum-extra/src/middleware.rs | 4 +--- axum-extra/src/routing/mod.rs | 3 +-- axum/src/extension.rs | 4 ++-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb7c56b017..b01522e4a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ manual_let_else = "warn" option_if_let_else = "warn" missing_const_for_fn = "warn" if_not_else = "warn" +map_unwrap_or = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-extra/src/extract/query.rs b/axum-extra/src/extract/query.rs index 3b0330acfb..39eab260ca 100644 --- a/axum-extra/src/extract/query.rs +++ b/axum-extra/src/extract/query.rs @@ -269,8 +269,7 @@ mod tests { let app = Router::new().route( "/", post(|OptionalQuery(data): OptionalQuery| async move { - data.map(|Data { values }| values.join(",")) - .unwrap_or("None".to_owned()) + data.map_or("None".to_owned(), |Data { values }| values.join(",")) }), ); diff --git a/axum-extra/src/middleware.rs b/axum-extra/src/middleware.rs index 0303d484fd..de296959c4 100644 --- a/axum-extra/src/middleware.rs +++ b/axum-extra/src/middleware.rs @@ -38,7 +38,5 @@ use tower_layer::Identity; /// [`HandleErrorLayer`]: axum::error_handling::HandleErrorLayer /// [`Infallible`]: std::convert::Infallible pub fn option_layer(layer: Option) -> Either { - layer - .map(Either::E1) - .unwrap_or_else(|| Either::E2(Identity::new())) + layer.map_or_else(|| Either::E2(Identity::new()), Either::E1) } diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index b953236f1d..73cb6b59d8 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -367,8 +367,7 @@ where async fn redirect_handler(OriginalUri(uri): OriginalUri) -> Response { let new_uri = map_path(uri, |path| { path.strip_suffix('/') - .map(Cow::Borrowed) - .unwrap_or_else(|| Cow::Owned(format!("{path}/"))) + .map_or_else(|| Cow::Owned(format!("{path}/")), Cow::Borrowed) }); new_uri.map_or_else( diff --git a/axum/src/extension.rs b/axum/src/extension.rs index da3b7b0ddc..f62b6519aa 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -204,7 +204,7 @@ mod tests { } async fn optional_foo(extension: Option>) -> String { - extension.map(|foo| foo.0 .0).unwrap_or("none".to_owned()) + extension.map_or("none".to_owned(), |foo| foo.0 .0) } async fn requires_bar(Extension(bar): Extension) -> String { @@ -212,7 +212,7 @@ mod tests { } async fn optional_bar(extension: Option>) -> String { - extension.map(|bar| bar.0 .0).unwrap_or("none".to_owned()) + extension.map_or("none".to_owned(), |bar| bar.0 .0) } let app = Router::new() From 369f12d418957f995327c8d14e0ea2b2cf10eebe Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 17:05:25 +0200 Subject: [PATCH 13/19] style: add semicolons when nothing is returned Although these semicolons are optional, they aren't optional when extending the block with new code. To avoid having to change the previous last lines when adding more code to these functions, we should add the optional semicolon. This fixes the `semicolon_if_nothing_returned` clippy lint. NOTE: I `#[allow]`ed the `forever` function. Adding a semicolon at the end of `forever` causes a type inference error (E0282). --- Cargo.toml | 1 + axum/src/extract/raw_form.rs | 2 +- axum/src/extract/ws.rs | 2 +- axum/src/response/mod.rs | 2 +- axum/src/response/redirect.rs | 2 +- axum/src/routing/tests/handle_error.rs | 2 ++ axum/src/routing/tests/mod.rs | 2 +- axum/src/test_helpers/test_client.rs | 2 +- 8 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b01522e4a8..8528aa33bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ option_if_let_else = "warn" missing_const_for_fn = "warn" if_not_else = "warn" map_unwrap_or = "warn" +semicolon_if_nothing_returned = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum/src/extract/raw_form.rs b/axum/src/extract/raw_form.rs index 29cb4c6dd3..cbb71585d7 100644 --- a/axum/src/extract/raw_form.rs +++ b/axum/src/extract/raw_form.rs @@ -99,6 +99,6 @@ mod tests { assert!(matches!( RawForm::from_request(req, &()).await.unwrap_err(), RawFormRejection::InvalidFormContentType(InvalidFormContentType) - )) + )); } } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 99c582c778..6c608c4e8e 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -392,7 +392,7 @@ where F: FnOnce(Error) + Send + 'static, { fn call(self, error: Error) { - self(error) + self(error); } } diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs index 70be745200..45f501a3ac 100644 --- a/axum/src/response/mod.rs +++ b/axum/src/response/mod.rs @@ -252,6 +252,6 @@ mod tests { assert_eq!( super::NoContent.into_response().status(), StatusCode::NO_CONTENT, - ) + ); } } diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index 086c6ef78a..528639e819 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -125,7 +125,7 @@ mod tests { fn correct_location() { assert_eq!(EXAMPLE_URL, Redirect::permanent(EXAMPLE_URL).location()); - assert_eq!("/redirect", Redirect::permanent("/redirect").location()) + assert_eq!("/redirect", Redirect::permanent("/redirect").location()); } #[test] diff --git a/axum/src/routing/tests/handle_error.rs b/axum/src/routing/tests/handle_error.rs index a2fd2e6828..dbf676ec76 100644 --- a/axum/src/routing/tests/handle_error.rs +++ b/axum/src/routing/tests/handle_error.rs @@ -4,6 +4,8 @@ use tower::timeout::TimeoutLayer; async fn unit() {} +// Using a semicolon causes an error. +#[allow(clippy::semicolon_if_nothing_returned)] async fn forever() { pending().await } diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 969710627e..00b84a9fd8 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -1090,7 +1090,7 @@ async fn logging_rejections() { level: "TRACE".to_owned(), }, ]) - ) + ); } // https://github.com/tokio-rs/axum/issues/1955 diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 5fc4cf5a4f..fd0d45b15f 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -23,7 +23,7 @@ where tokio::spawn(async move { serve(listener, Shared::new(svc)) .await - .expect("server error") + .expect("server error"); }); addr From f54a09878e189ffa6f29cd4fae1d83ec83207dfd Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 17:13:57 +0200 Subject: [PATCH 14/19] style: use `assert!` instead of `if..panic!` `assert!` is much more concise and readable than manually triggering a panic via an `if` statement. Once I did this, clippy also found a couple of conditions that could be simplified. I applied those simplifications. This fixes the `manual_assert` clippy lint. --- Cargo.toml | 1 + axum-extra/src/routing/mod.rs | 18 +++++++-------- axum/benches/benches.rs | 4 +--- axum/src/response/sse.rs | 35 +++++++++++++++++------------- axum/src/routing/method_routing.rs | 9 ++++---- axum/src/routing/mod.rs | 14 +++++++----- axum/src/routing/path_router.rs | 9 ++++---- 7 files changed, 47 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8528aa33bb..e6c9a1c846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ missing_const_for_fn = "warn" if_not_else = "warn" map_unwrap_or = "warn" semicolon_if_nothing_returned = "warn" +manual_assert = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index 73cb6b59d8..6d701b597c 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -30,12 +30,11 @@ pub use self::typed::{SecondElementIs, TypedPath}; #[doc(hidden)] #[must_use] pub const fn __private_validate_static_path(path: &'static str) -> &'static str { - if path.is_empty() { - panic!("Paths must start with a `/`. Use \"/\" for root routes") - } - if path.as_bytes()[0] != b'/' { - panic!("Paths must start with /"); - } + assert!( + !path.is_empty(), + "Paths must start with a `/`. Use \"/\" for root routes" + ); + assert!(path.as_bytes()[0] == b'/', "Paths must start with /"); path } @@ -355,9 +354,10 @@ where #[track_caller] fn validate_tsr_path(path: &str) { - if path == "/" { - panic!("Cannot add a trailing slash redirect route for `/`") - } + assert!( + path != "/", + "Cannot add a trailing slash redirect route for `/`" + ); } fn add_tsr_redirect_route(router: Router, path: &str) -> Router diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index dd82a221b4..c4376afd43 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -230,9 +230,7 @@ fn install_rewrk() { let status = cmd .status() .unwrap_or_else(|_| panic!("failed to install rewrk")); - if !status.success() { - panic!("failed to install rewrk"); - } + assert!(status.success(), "failed to install rewrk"); } fn ensure_rewrk_is_installed() { diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 823234e53c..50f76ddcd5 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -203,9 +203,10 @@ impl Event { where T: AsRef, { - if self.flags.contains(EventFlags::HAS_DATA) { - panic!("Called `Event::data` multiple times"); - } + assert!( + !self.flags.contains(EventFlags::HAS_DATA), + "Called `Event::data` multiple times" + ); for line in memchr_split(b'\n', data.as_ref().as_bytes()) { self.field("data", line); @@ -246,9 +247,10 @@ impl Event { self.0.flush() } } - if self.flags.contains(EventFlags::HAS_DATA) { - panic!("Called `Event::json_data` multiple times"); - } + assert!( + !self.flags.contains(EventFlags::HAS_DATA), + "Called `Event::json_data` multiple times" + ); let buffer = self.buffer.as_mut(); buffer.extend_from_slice(b"data: "); @@ -297,9 +299,10 @@ impl Event { where T: AsRef, { - if self.flags.contains(EventFlags::HAS_EVENT) { - panic!("Called `Event::event` multiple times"); - } + assert!( + !self.flags.contains(EventFlags::HAS_EVENT), + "Called `Event::event` multiple times" + ); self.flags.insert(EventFlags::HAS_EVENT); self.field("event", event.as_ref()); @@ -317,9 +320,10 @@ impl Event { /// /// Panics if this function has already been called on this event. pub fn retry(mut self, duration: Duration) -> Self { - if self.flags.contains(EventFlags::HAS_RETRY) { - panic!("Called `Event::retry` multiple times"); - } + assert!( + !self.flags.contains(EventFlags::HAS_RETRY), + "Called `Event::retry` multiple times" + ); self.flags.insert(EventFlags::HAS_RETRY); let buffer = self.buffer.as_mut(); @@ -364,9 +368,10 @@ impl Event { where T: AsRef, { - if self.flags.contains(EventFlags::HAS_ID) { - panic!("Called `Event::id` multiple times"); - } + assert!( + !self.flags.contains(EventFlags::HAS_ID), + "Called `Event::id` multiple times" + ); self.flags.insert(EventFlags::HAS_ID); let id = id.as_ref().as_bytes(); diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index db089e0dee..d0718feffa 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -836,12 +836,11 @@ where S: Clone, { if endpoint_filter.contains(filter) { - if out.is_some() { - panic!( - "Overlapping method route. Cannot add two method routes that both handle \ + assert!( + !out.is_some(), + "Overlapping method route. Cannot add two method routes that both handle \ `{method_name}`", - ) - } + ); *out = endpoint.clone(); for method in methods { append_allow_header(allow_header, method); diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 7a0b5cf1c9..2cd8f3860a 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -203,9 +203,10 @@ where #[doc(alias = "scope")] // Some web frameworks like actix-web use this term #[track_caller] pub fn nest(self, path: &str, router: Self) -> Self { - if path.is_empty() || path == "/" { - panic!("Nesting at the root is no longer supported. Use merge instead."); - } + assert!( + !(path.is_empty() || path == "/"), + "Nesting at the root is no longer supported. Use merge instead." + ); let RouterInner { path_router, @@ -229,9 +230,10 @@ where T::Response: IntoResponse, T::Future: Send + 'static, { - if path.is_empty() || path == "/" { - panic!("Nesting at the root is no longer supported. Use fallback_service instead."); - } + assert!( + !(path.is_empty() || path == "/"), + "Nesting at the root is no longer supported. Use fallback_service instead." + ); tap_inner!(self, mut this => { panic_on_err!(this.path_router.nest_service(path, service)); diff --git a/axum/src/routing/path_router.rs b/axum/src/routing/path_router.rs index 5999528193..2e85974e36 100644 --- a/axum/src/routing/path_router.rs +++ b/axum/src/routing/path_router.rs @@ -282,12 +282,11 @@ where >::Error: Into + 'static, >::Future: Send + 'static, { - if self.routes.is_empty() { - panic!( - "Adding a route_layer before any routes is a no-op. \ + assert!( + !self.routes.is_empty(), + "Adding a route_layer before any routes is a no-op. \ Add the routes you want the layer to apply to first." - ); - } + ); let routes = self .routes From e5973a24b2ce486a65c9b55ff2898d34dec6c9bd Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 17:22:47 +0200 Subject: [PATCH 15/19] fix: explicitly match with `()` instead of `_` Using `()` makes it clearer to both the reader and the compiler that the pattern contains no data. It will also trigger a compilation error if the type changes, which makes the pattern-matching more robust, safe, and less prone to uncaught logic errors. This fixes the `ignored_unit_patterns` clippy lint. --- Cargo.toml | 1 + axum-core/src/body.rs | 2 +- axum-core/src/ext_traits/request_parts.rs | 4 ++-- axum-extra/src/extract/cached.rs | 2 +- axum-extra/src/extract/with_rejection.rs | 4 ++-- axum/src/routing/tests/handle_error.rs | 2 +- axum/src/serve/mod.rs | 4 ++-- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6c9a1c846..f37faeadb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ if_not_else = "warn" map_unwrap_or = "warn" semicolon_if_nothing_returned = "warn" manual_assert = "warn" +ignored_unit_patterns = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 2566b48559..0ae8729655 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -85,7 +85,7 @@ impl Default for Body { } impl From<()> for Body { - fn from(_: ()) -> Self { + fn from((): ()) -> Self { Self::empty() } } diff --git a/axum-core/src/ext_traits/request_parts.rs b/axum-core/src/ext_traits/request_parts.rs index 9e1a3d1c16..528f0bd63d 100644 --- a/axum-core/src/ext_traits/request_parts.rs +++ b/axum-core/src/ext_traits/request_parts.rs @@ -147,7 +147,7 @@ mod tests { #[tokio::test] async fn extract_without_state() { - let (mut parts, _) = Request::new(()).into_parts(); + let (mut parts, ()) = Request::new(()).into_parts(); let method: Method = parts.extract().await.unwrap(); @@ -156,7 +156,7 @@ mod tests { #[tokio::test] async fn extract_with_state() { - let (mut parts, _) = Request::new(()).into_parts(); + let (mut parts, ()) = Request::new(()).into_parts(); let state = "state".to_owned(); diff --git a/axum-extra/src/extract/cached.rs b/axum-extra/src/extract/cached.rs index 64b4c3056f..d0f44e446f 100644 --- a/axum-extra/src/extract/cached.rs +++ b/axum-extra/src/extract/cached.rs @@ -133,7 +133,7 @@ mod tests { } } - let (mut parts, _) = Request::new(()).into_parts(); + let (mut parts, ()) = Request::new(()).into_parts(); let first = Cached::::from_request_parts(&mut parts, &()) .await diff --git a/axum-extra/src/extract/with_rejection.rs b/axum-extra/src/extract/with_rejection.rs index ee9906682a..1084801cb3 100644 --- a/axum-extra/src/extract/with_rejection.rs +++ b/axum-extra/src/extract/with_rejection.rs @@ -187,7 +187,7 @@ mod tests { } impl From<()> for TestRejection { - fn from(_: ()) -> Self { + fn from((): ()) -> Self { Self } } @@ -196,7 +196,7 @@ mod tests { let result = WithRejection::::from_request(req, &()).await; assert!(matches!(result, Err(TestRejection))); - let (mut parts, _) = Request::new(()).into_parts(); + let (mut parts, ()) = Request::new(()).into_parts(); let result = WithRejection::::from_request_parts(&mut parts, &()) .await; diff --git a/axum/src/routing/tests/handle_error.rs b/axum/src/routing/tests/handle_error.rs index dbf676ec76..530fa84f4e 100644 --- a/axum/src/routing/tests/handle_error.rs +++ b/axum/src/routing/tests/handle_error.rs @@ -85,7 +85,7 @@ async fn handler_multiple_methods_last() { async fn handler_service_ext() { let fallible_service = tower::service_fn(|_| async { Err::<(), ()>(()) }); let handle_error_service = - fallible_service.handle_error(|_| async { StatusCode::INTERNAL_SERVER_ERROR }); + fallible_service.handle_error(|()| async { StatusCode::INTERNAL_SERVER_ERROR }); let app = Router::new().route("/", get_service(handle_error_service)); diff --git a/axum/src/serve/mod.rs b/axum/src/serve/mod.rs index b6eee7bd06..311e5834db 100644 --- a/axum/src/serve/mod.rs +++ b/axum/src/serve/mod.rs @@ -298,7 +298,7 @@ where loop { let (io, remote_addr) = tokio::select! { conn = listener.accept() => conn, - _ = signal_tx.closed() => { + () = signal_tx.closed() => { trace!("signal received, not accepting new connections"); break; } @@ -424,7 +424,7 @@ async fn handle_connection( } break; } - _ = &mut signal_closed => { + () = &mut signal_closed => { trace!("signal received in task, starting graceful shutdown"); conn.as_mut().graceful_shutdown(); } From f978792608b223e03b764348537d7a205d6dd2bc Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 17:51:40 +0200 Subject: [PATCH 16/19] refactor: don't use wildcard imports Wildcard imports can pollute the namespace, and can lead to confusing error messages or even unexpected behaviour. This commit uses explicit imports instead. Although slightly more verbose, it can save a lot of debugging efforts in the future. Exceptions to this rule are prelude imports, imports of `pub use` `re-exports`, and `super::*` imports in tests. This fixes the `wildcard_imports` clippy lint. --- Cargo.toml | 1 + axum-core/src/extract/request_parts.rs | 5 ++++- axum/src/extension.rs | 5 ++++- axum/src/extract/matched_path.rs | 5 ++++- axum/src/extract/path/mod.rs | 5 ++++- axum/src/extract/query.rs | 5 ++++- axum/src/extract/ws.rs | 6 +++++- axum/src/form.rs | 8 +++++++- axum/src/json.rs | 5 ++++- 9 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f37faeadb7..67f07253d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ map_unwrap_or = "warn" semicolon_if_nothing_returned = "warn" manual_assert = "warn" ignored_unit_patterns = "warn" +wildcard_imports = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index c93a474964..c3536526d4 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -1,4 +1,7 @@ -use super::{rejection::*, FromRequest, FromRequestParts, Request}; +use super::{ + rejection::{BytesRejection, FailedToBufferBody, InvalidUtf8, StringRejection}, + FromRequest, FromRequestParts, Request, +}; use crate::{body::Body, RequestExt}; use bytes::{BufMut, Bytes, BytesMut}; use http::{request::Parts, Extensions, HeaderMap, Method, Uri, Version}; diff --git a/axum/src/extension.rs b/axum/src/extension.rs index f62b6519aa..bbdd05d015 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -1,4 +1,7 @@ -use crate::{extract::rejection::*, response::IntoResponseParts}; +use crate::{ + extract::rejection::{ExtensionRejection, MissingExtension}, + response::IntoResponseParts, +}; use axum_core::extract::OptionalFromRequestParts; use axum_core::{ extract::FromRequestParts, diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 3f9a03801a..55857996d9 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -1,4 +1,7 @@ -use super::{rejection::*, FromRequestParts}; +use super::{ + rejection::{MatchedPathMissing, MatchedPathRejection}, + FromRequestParts, +}; use crate::routing::{RouteId, NEST_TAIL_PARAM_CAPTURE}; use axum_core::extract::OptionalFromRequestParts; use http::request::Parts; diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 2514e52866..a5a40cd3fd 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -4,7 +4,10 @@ mod de; use crate::{ - extract::{rejection::*, FromRequestParts}, + extract::{ + rejection::{MissingPathParams, PathRejection, RawPathParamsRejection}, + FromRequestParts, + }, routing::url_params::UrlParams, util::PercentDecodedStr, }; diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index b06c7f3e53..ddc9425a46 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -1,4 +1,7 @@ -use super::{rejection::*, FromRequestParts}; +use super::{ + rejection::{FailedToDeserializeQueryString, QueryRejection}, + FromRequestParts, +}; use http::{request::Parts, Uri}; use serde::de::DeserializeOwned; diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 6c608c4e8e..5865907ed3 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -90,7 +90,11 @@ //! //! [`StreamExt::split`]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.split -use self::rejection::*; +use self::rejection::{ + ConnectionNotUpgradable, InvalidConnectionHeader, InvalidProtocolPseudoheader, + InvalidUpgradeHeader, InvalidWebSocketVersionHeader, MethodNotConnect, MethodNotGet, + WebSocketKeyHeaderMissing, WebSocketUpgradeRejection, +}; use super::FromRequestParts; use crate::{body::Bytes, response::Response, Error}; use axum_core::body::Body; diff --git a/axum/src/form.rs b/axum/src/form.rs index eb499def73..6d4b4f4698 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -1,5 +1,10 @@ use crate::extract::Request; -use crate::extract::{rejection::*, FromRequest, RawForm}; +use crate::extract::{ + rejection::{ + FailedToDeserializeForm, FailedToDeserializeFormBody, FormRejection, RawFormRejection, + }, + FromRequest, RawForm, +}; use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use http::header::CONTENT_TYPE; @@ -130,6 +135,7 @@ axum_core::__impl_deref!(Form); #[cfg(test)] mod tests { use crate::{ + extract::rejection::InvalidFormContentType, routing::{on, MethodFilter}, test_helpers::TestClient, Router, diff --git a/axum/src/json.rs b/axum/src/json.rs index 00126f09b5..54411f25e0 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -1,5 +1,8 @@ use crate::extract::Request; -use crate::extract::{rejection::*, FromRequest}; +use crate::extract::{ + rejection::{JsonDataError, JsonRejection, JsonSyntaxError, MissingJsonContentType}, + FromRequest, +}; use axum_core::extract::OptionalFromRequest; use axum_core::response::{IntoResponse, Response}; use bytes::{BufMut, Bytes, BytesMut}; From 19250419bc3a829ef6783051180be03590b819df Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 18:06:46 +0200 Subject: [PATCH 17/19] fix: add #[must_use] to methods returning `Self` Methods returning `Self` often create new instances of a type without causing any side-effects (such as interacting with other types). Unlike for ede4d8545ece482aa9894777860b889bdc1c0774, it is almost guaranteed that the user intended to use the newly created value, and simply just forgot. This fixes the `return_self_not_must_use` clippy lint. See-also: ede4d8545ece482aa9894777860b889bdc1c0774 --- Cargo.toml | 1 + axum-extra/src/response/file_stream.rs | 2 ++ axum-extra/src/routing/mod.rs | 11 +++++++++++ axum/src/extract/ws.rs | 7 +++++++ axum/src/test_helpers/test_client.rs | 4 ++++ 5 files changed, 25 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 67f07253d3..a09262f45c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ semicolon_if_nothing_returned = "warn" manual_assert = "warn" ignored_unit_patterns = "warn" wildcard_imports = "warn" +return_self_not_must_use = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-extra/src/response/file_stream.rs b/axum-extra/src/response/file_stream.rs index 5df0977bfc..59ece3fd44 100644 --- a/axum-extra/src/response/file_stream.rs +++ b/axum-extra/src/response/file_stream.rs @@ -118,12 +118,14 @@ where /// Set the file name of the [`FileStream`]. /// /// This adds the attachment `Content-Disposition` header with the given `file_name`. + #[must_use] pub fn file_name(mut self, file_name: impl Into) -> Self { self.file_name = Some(file_name.into()); self } /// Set the size of the file. + #[must_use] pub const fn content_size(mut self, len: u64) -> Self { self.content_size = Some(len); self diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index 6d701b597c..bcac7d3c8a 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -84,6 +84,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_get(self, handler: H) -> Self where H: axum::handler::Handler, @@ -97,6 +98,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_delete(self, handler: H) -> Self where H: axum::handler::Handler, @@ -110,6 +112,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_head(self, handler: H) -> Self where H: axum::handler::Handler, @@ -123,6 +126,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_options(self, handler: H) -> Self where H: axum::handler::Handler, @@ -136,6 +140,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_patch(self, handler: H) -> Self where H: axum::handler::Handler, @@ -149,6 +154,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_post(self, handler: H) -> Self where H: axum::handler::Handler, @@ -162,6 +168,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_put(self, handler: H) -> Self where H: axum::handler::Handler, @@ -175,6 +182,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_trace(self, handler: H) -> Self where H: axum::handler::Handler, @@ -188,6 +196,7 @@ pub trait RouterExt: sealed::Sealed { /// /// See [`TypedPath`] for more details and examples. #[cfg(feature = "typed-routing")] + #[must_use] fn typed_connect(self, handler: H) -> Self where H: axum::handler::Handler, @@ -219,6 +228,7 @@ pub trait RouterExt: sealed::Sealed { /// .route_with_tsr("/bar/", get(|| async {})); /// # let _: Router = app; /// ``` + #[must_use] fn route_with_tsr(self, path: &str, method_router: MethodRouter) -> Self where Self: Sized; @@ -226,6 +236,7 @@ pub trait RouterExt: sealed::Sealed { /// Add another route to the router with an additional "trailing slash redirect" route. /// /// This works like [`RouterExt::route_with_tsr`] but accepts any [`Service`]. + #[must_use] fn route_service_with_tsr(self, path: &str, service: T) -> Self where T: Service + Clone + Send + Sync + 'static, diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 5865907ed3..0ce6c1b6a5 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -156,6 +156,7 @@ impl std::fmt::Debug for WebSocketUpgrade { impl WebSocketUpgrade { /// Read buffer capacity. The default value is 128KiB + #[must_use] pub const fn read_buffer_size(mut self, size: usize) -> Self { self.config.read_buffer_size = size; self @@ -170,6 +171,7 @@ impl WebSocketUpgrade { /// It is often more optimal to allow them to buffer a little, hence the default value. /// /// Note: [`flush`](SinkExt::flush) will always fully write the buffer regardless. + #[must_use] pub const fn write_buffer_size(mut self, size: usize) -> Self { self.config.write_buffer_size = size; self @@ -186,24 +188,28 @@ impl WebSocketUpgrade { /// /// Note: Should always be at least [`write_buffer_size + 1 message`](Self::write_buffer_size) /// and probably a little more depending on error handling strategy. + #[must_use] pub const fn max_write_buffer_size(mut self, max: usize) -> Self { self.config.max_write_buffer_size = max; self } /// Set the maximum message size (defaults to 64 megabytes) + #[must_use] pub const fn max_message_size(mut self, max: usize) -> Self { self.config.max_message_size = Some(max); self } /// Set the maximum frame size (defaults to 16 megabytes) + #[must_use] pub const fn max_frame_size(mut self, max: usize) -> Self { self.config.max_frame_size = Some(max); self } /// Allow server to accept unmasked frames (defaults to false) + #[must_use] pub const fn accept_unmasked_frames(mut self, accept: bool) -> Self { self.config.accept_unmasked_frames = accept; self @@ -239,6 +245,7 @@ impl WebSocketUpgrade { /// } /// # let _: Router = app; /// ``` + #[must_use] pub fn protocols(mut self, protocols: I) -> Self where I: IntoIterator, diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index fd0d45b15f..d23ae5b2a8 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -99,11 +99,13 @@ pub struct RequestBuilder { } impl RequestBuilder { + #[must_use] pub fn body(mut self, body: impl Into) -> Self { self.builder = self.builder.body(body); self } + #[must_use] pub fn json(mut self, json: &T) -> Self where T: serde::Serialize, @@ -112,6 +114,7 @@ impl RequestBuilder { self } + #[must_use] pub fn header(mut self, key: K, value: V) -> Self where HeaderName: TryFrom, @@ -123,6 +126,7 @@ impl RequestBuilder { self } + #[must_use] #[allow(dead_code)] pub fn multipart(mut self, form: reqwest::multipart::Form) -> Self { self.builder = self.builder.multipart(form); From 0365e0b6f9060e6b00651f2ecee79e3951e5e989 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 18:27:55 +0200 Subject: [PATCH 18/19] docs: use markdown for code and types - Add backticks to the documentation for `Utf8Bytes` - Wrap the example of a `range` in a code block - [Exclude `WebSocket`](https://github.com/tokio-rs/axum/pull/3394#issuecomment-3028420921) from being seen as an identifier by clippy. This fixes the `doc_markdown` clippy lint. --- .clippy.toml | 1 + Cargo.toml | 1 + axum-extra/src/response/file_stream.rs | 2 ++ axum/src/extract/ws.rs | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index b22159d2ec..70820aab2e 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -2,3 +2,4 @@ allow-mixed-uninlined-format-args = false disallowed-types = [ { path = "tower::util::BoxCloneService", reason = "Use tower::util::BoxCloneSyncService instead" }, ] +doc-valid-idents = ["WebSocket", "WebSockets", ".."] diff --git a/Cargo.toml b/Cargo.toml index a09262f45c..6482b32038 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ manual_assert = "warn" ignored_unit_patterns = "warn" wildcard_imports = "warn" return_self_not_must_use = "warn" +doc_markdown = "warn" # configuration for https://github.com/crate-ci/typos [workspace.metadata.typos.default.extend-identifiers] diff --git a/axum-extra/src/response/file_stream.rs b/axum-extra/src/response/file_stream.rs index 59ece3fd44..7b0e512442 100644 --- a/axum-extra/src/response/file_stream.rs +++ b/axum-extra/src/response/file_stream.rs @@ -133,7 +133,9 @@ where /// Return a range response. /// + /// ```rust /// range: (start, end, total_size) + /// ``` /// /// # Examples /// diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 0ce6c1b6a5..ec40a12715 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -575,7 +575,7 @@ impl Sink for WebSocket { /// UTF-8 wrapper for [Bytes]. /// -/// An [Utf8Bytes] is always guaranteed to contain valid UTF-8. +/// An [`Utf8Bytes`] is always guaranteed to contain valid UTF-8. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Utf8Bytes(ts::Utf8Bytes); @@ -809,7 +809,7 @@ impl Message { } } - /// Attempt to consume the WebSocket message and convert it to a Utf8Bytes. + /// Attempt to consume the WebSocket message and convert it to a [`Utf8Bytes`]. pub fn into_text(self) -> Result { match self { Self::Text(string) => Ok(string), From 59908042d7317e81e7b791e5391b41bc47c4a14f Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 18:32:33 +0200 Subject: [PATCH 19/19] fix: respect feature flags in the imports f978792608b223e03b764348537d7a205d6dd2bc caused a compilation warning to appear when compiling without the `http2` feature flag. This commit fixes that by only importing `InvalidProtocolPseudoheader` when the `http2` feature is enabled. --- axum/src/extract/ws.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index ec40a12715..12fe6fd3b3 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -91,10 +91,12 @@ //! [`StreamExt::split`]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.split use self::rejection::{ - ConnectionNotUpgradable, InvalidConnectionHeader, InvalidProtocolPseudoheader, + ConnectionNotUpgradable, InvalidConnectionHeader, InvalidUpgradeHeader, InvalidWebSocketVersionHeader, MethodNotConnect, MethodNotGet, WebSocketKeyHeaderMissing, WebSocketUpgradeRejection, }; +#[cfg(feature = "http2")] +use self::rejection::InvalidProtocolPseudoheader; use super::FromRequestParts; use crate::{body::Bytes, response::Response, Error}; use axum_core::body::Body;