Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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", ".."]
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ uninlined_format_args = "warn"
unnested_or_patterns = "warn"
unused_self = "warn"
verbose_file_reads = "warn"
must_use_candidate = "warn"
use_self = "warn"
redundant_else = "warn"
unnecessary_semicolon = "warn"
manual_let_else = "warn"
option_if_let_else = "warn"
missing_const_for_fn = "warn"
if_not_else = "warn"
map_unwrap_or = "warn"
semicolon_if_nothing_returned = "warn"
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]
Expand Down
15 changes: 8 additions & 7 deletions axum-core/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ where
K: Send + 'static,
{
let mut k = Some(k);
if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k) {
Ok(k.take().unwrap())
} else {
Err(k.unwrap())
}

<dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k)
.and_then(Option::take)
.map_or_else(|| Err(k.unwrap()), Ok)
}

/// The body type used in axum requests and responses.
Expand All @@ -48,6 +47,7 @@ impl Body {
}

/// Create an empty body.
#[must_use]
pub fn empty() -> Self {
Self::new(http_body_util::Empty::new())
}
Expand All @@ -72,7 +72,8 @@ 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
pub fn into_data_stream(self) -> BodyDataStream {
#[must_use]
pub const fn into_data_stream(self) -> BodyDataStream {
BodyDataStream { inner: self }
}
}
Expand All @@ -84,7 +85,7 @@ impl Default for Body {
}

impl From<()> for Body {
fn from(_: ()) -> Self {
fn from((): ()) -> Self {
Self::empty()
}
}
Expand Down
1 change: 1 addition & 0 deletions axum-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ impl Error {
}

/// Convert an `Error` back into the underlying boxed trait object.
#[must_use]
pub fn into_inner(self) -> BoxError {
Comment on lines +19 to 20
Copy link
Collaborator

@mladedav mladedav Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For errors (and possibly other cases such as builders) we might want to add #[must_use] to the type rather than methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to avoid #[must_use] for the methods here, since BoxError is a type alias and not a "real" type. Adding #[must_use] to the Error struct doesn't do anything, either. Any ideas?

self.inner
}
Expand Down
7 changes: 5 additions & 2 deletions axum-core/src/ext_traits/mod.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions axum-core/src/ext_traits/request_parts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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();

Expand Down
4 changes: 2 additions & 2 deletions axum-core/src/extract/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ where
fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> impl Future<Output = Result<Option<T>, Self::Rejection>> {
) -> impl Future<Output = Result<Self, Self::Rejection>> {
T::from_request_parts(parts, state)
}
}
Expand All @@ -57,7 +57,7 @@ where
{
type Rejection = T::Rejection;

async fn from_request(req: Request, state: &S) -> Result<Option<T>, Self::Rejection> {
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
T::from_request(req, state).await
}
}
9 changes: 6 additions & 3 deletions axum-core/src/extract/request_parts.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -73,7 +76,7 @@ where

async fn from_request(req: Request, _: &S) -> Result<Self, Self::Rejection> {
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)
}
Expand Down Expand Up @@ -128,7 +131,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)
}
Expand Down
2 changes: 1 addition & 1 deletion axum-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub mod response;
/// Alias for a type-erased error type.
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;

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;
10 changes: 7 additions & 3 deletions axum-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Collaborator

@mladedav mladedav Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think most of these really make sense. I don't think anyone would build a rejection in a const context to then call this.

I did not go through all of this commit though, there might be some useful ones here, but this lint might also be one to split out of this PR.

http::StatusCode::$status
}
}
Expand Down Expand Up @@ -106,12 +106,14 @@ 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.
pub fn status(&self) -> http::StatusCode {
#[must_use]
pub const fn status(&self) -> http::StatusCode {
http::StatusCode::$status
}
}
Expand Down Expand Up @@ -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 {
$(
Expand All @@ -188,7 +191,8 @@ macro_rules! __composite_rejection {
}

/// Get the status code used for this rejection.
pub fn status(&self) -> http::StatusCode {
#[must_use]
pub const fn status(&self) -> http::StatusCode {
match self {
$(
Self::$variant(inner) => inner.status(),
Expand Down
4 changes: 2 additions & 2 deletions axum-core/src/response/into_response_parts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ pub struct TryIntoHeaderError<K, V> {
}

impl<K, V> TryIntoHeaderError<K, V> {
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),
}
Expand Down
12 changes: 6 additions & 6 deletions axum-extra/src/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
}
}
}
Expand All @@ -300,15 +300,15 @@ where

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
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)),
}
}
}
2 changes: 1 addition & 1 deletion axum-extra/src/extract/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ mod tests {
}
}

let (mut parts, _) = Request::new(()).into_parts();
let (mut parts, ()) = Request::new(()).into_parts();

let first = Cached::<Extractor>::from_request_parts(&mut parts, &())
.await
Expand Down
7 changes: 5 additions & 2 deletions axum-extra/src/extract/cookie/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -135,6 +136,7 @@ impl CookieJar {
/// CookieJar`.
///
/// [`FromRequestParts`]: axum::extract::FromRequestParts
#[must_use]
pub fn new() -> Self {
Self::default()
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -321,13 +324,13 @@ mod tests {
}

impl FromRef<AppState> for Key {
fn from_ref(state: &AppState) -> Key {
fn from_ref(state: &AppState) -> Self {
state.key.clone()
}
}

impl FromRef<AppState> for CustomKey {
fn from_ref(state: &AppState) -> CustomKey {
fn from_ref(state: &AppState) -> Self {
state.custom_key.clone()
}
}
Expand Down
6 changes: 5 additions & 1 deletion axum-extra/src/extract/cookie/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ where
key,
_marker: _,
} = PrivateCookieJar::from_headers(&parts.headers, key);
Ok(PrivateCookieJar {
Ok(Self {
jar,
key,
_marker: PhantomData,
Expand All @@ -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);
Expand All @@ -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(),
Expand All @@ -201,6 +203,7 @@ impl<K> PrivateCookieJar<K> {
/// .map(|cookie| cookie.value().to_owned());
/// }
/// ```
#[must_use]
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
self.private_jar().get(name)
}
Expand Down Expand Up @@ -246,6 +249,7 @@ impl<K> PrivateCookieJar<K> {

/// Authenticates and decrypts `cookie`, returning the plaintext version if decryption succeeds
/// or `None` otherwise.
#[must_use]
pub fn decrypt(&self, cookie: Cookie<'static>) -> Option<Cookie<'static>> {
self.private_jar().decrypt(cookie)
}
Expand Down
6 changes: 5 additions & 1 deletion axum-extra/src/extract/cookie/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ where
key,
_marker: _,
} = SignedCookieJar::from_headers(&parts.headers, key);
Ok(SignedCookieJar {
Ok(Self {
jar,
key,
_marker: PhantomData,
Expand All @@ -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);
Expand All @@ -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(),
Expand Down Expand Up @@ -219,6 +221,7 @@ impl<K> SignedCookieJar<K> {
/// .map(|cookie| cookie.value().to_owned());
/// }
/// ```
#[must_use]
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
self.signed_jar().get(name)
}
Expand Down Expand Up @@ -264,6 +267,7 @@ impl<K> SignedCookieJar<K> {

/// 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<Cookie<'static>> {
self.signed_jar().verify(cookie)
}
Expand Down
Loading
Loading