Skip to content

Commit

Permalink
Merge branch 'main' into tungstenite++
Browse files Browse the repository at this point in the history
  • Loading branch information
davidpdrsn committed Jun 22, 2023
2 parents 430e14c + 68696b0 commit 67d15ac
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 9 deletions.
42 changes: 41 additions & 1 deletion axum-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,54 @@ macro_rules! __composite_rejection {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
$(
Self::$variant(inner) => Some(inner),
Self::$variant(inner) => inner.source(),
)+
}
}
}
};
}

#[cfg(test)]
mod composite_rejection_tests {
use self::defs::*;
use crate::Error;
use std::error::Error as _;

#[allow(dead_code, unreachable_pub)]
mod defs {
use crate::{__composite_rejection, __define_rejection};

__define_rejection! {
#[status = BAD_REQUEST]
#[body = "error message 1"]
pub struct Inner1;
}
__define_rejection! {
#[status = BAD_REQUEST]
#[body = "error message 2"]
pub struct Inner2(Error);
}
__composite_rejection! {
pub enum Outer { Inner1, Inner2 }
}
}

/// The implementation of `.source()` on `Outer` should defer straight to the implementation
/// on its inner type instead of returning the inner type itself, because the `Display`
/// implementation on `Outer` already forwards to the inner type and so it would result in two
/// errors in the chain `Display`ing the same thing.
#[test]
fn source_gives_inner_source() {
let rejection = Outer::Inner1(Inner1);
assert!(rejection.source().is_none());

let msg = "hello world";
let rejection = Outer::Inner2(Inner2(Error::new(msg)));
assert_eq!(rejection.source().unwrap().to_string(), msg);
}
}

#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/src/extract/cookie/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData};
/// type Target = InnerState;
///
/// fn deref(&self) -> &Self::Target {
/// &*self.0
/// &self.0
/// }
/// }
///
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/src/extract/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::{

/// Extractor that parses `multipart/form-data` requests (commonly used with file uploads).
///
/// Since extracting multipart form data from the request requires consuming the body, the
/// ⚠️ Since extracting multipart form data from the request requires consuming the body, the
/// `Multipart` extractor must be *last* if there are multiple extractors in a handler.
/// See ["the order of extractors"][order-of-extractors]
///
Expand Down
8 changes: 8 additions & 0 deletions axum-macros/src/debug_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr

quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn #check_fn_generics()
where
Expand All @@ -272,6 +273,7 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr
// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #call_check_fn()
{
Expand Down Expand Up @@ -415,6 +417,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
let make = if item_fn.sig.asyncness.is_some() {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #make_value_name() -> #ty {
#declare_inputs
Expand All @@ -424,6 +427,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #make_value_name() -> #ty {
#declare_inputs
Expand All @@ -439,6 +443,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
#make

#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
let value = #receiver #make_value_name().await;
Expand All @@ -451,6 +456,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
#make
Expand Down Expand Up @@ -501,6 +507,7 @@ fn check_future_send(item_fn: &ItemFn) -> TokenStream {
if let Some(receiver) = self_receiver(item_fn) {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
let future = #receiver #handler_name(#(#args),*);
Expand All @@ -510,6 +517,7 @@ fn check_future_send(item_fn: &ItemFn) -> TokenStream {
} else {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
#item_fn
Expand Down
8 changes: 8 additions & 0 deletions axum-macros/tests/debug_handler/pass/deny_unreachable_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![deny(unreachable_code)]

use axum::extract::Path;

#[axum_macros::debug_handler]
async fn handler(Path(_): Path<String>) {}

fn main() {}
10 changes: 10 additions & 0 deletions axum/src/docs/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or
`Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in
axum.

Instead of a direct `StatusCode`, it makes sense to use intermediate error type
that can ultimately be converted to `Reponse`. This allows using `?` operator
in handlers. See those examples:

* [`anyhow-error-response`][anyhow] for generic boxed errors
* [`error-handling-and-dependency-injection`][ehdi] for application-specific detailed errors

[anyhow]:https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs
[ehdi]:https://github.com/tokio-rs/axum/blob/main/examples/error-handling-and-dependency-injection/src/main.rs

This also applies to extractors. If an extractor doesn't match the request the
request will be rejected and a response will be returned without calling your
handler. See [`extract`](crate::extract) to learn more about handling extractor
Expand Down
2 changes: 1 addition & 1 deletion axum/src/docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ That is:
- It then does its thing and passes the request onto `layer_two`
- Which passes the request onto `layer_one`
- Which passes the request onto `handler` where a response is produced
- That response is then passes to `layer_one`
- That response is then passed to `layer_one`
- Then to `layer_two`
- And finally to `layer_three` where it's returned out of your app

Expand Down
19 changes: 18 additions & 1 deletion axum/src/docs/routing/route.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ the path `/foo/bar/baz` the value of `rest` will be `bar/baz`.

# Accepting multiple methods

To accept multiple methods for the same route you must add all handlers at the
To accept multiple methods for the same route you can add all handlers at the
same time:

```rust
Expand All @@ -80,6 +80,23 @@ async fn delete_root() {}
# let _: Router = app;
```

Or you can add them one by one:

```rust
# use axum::Router;
# use axum::routing::{get, post, delete};
#
let app = Router::new()
.route("/", get(get_root))
.route("/", post(post_root))
.route("/", delete(delete_root));
#
# let _: Router = app;
# async fn get_root() {}
# async fn post_root() {}
# async fn delete_root() {}
```

# More examples

```rust
Expand Down
2 changes: 1 addition & 1 deletion axum/src/extract/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::{

/// Extractor that parses `multipart/form-data` requests (commonly used with file uploads).
///
/// Since extracting multipart form data from the request requires consuming the body, the
/// ⚠️ Since extracting multipart form data from the request requires consuming the body, the
/// `Multipart` extractor must be *last* if there are multiple extractors in a handler.
/// See ["the order of extractors"][order-of-extractors]
///
Expand Down
2 changes: 1 addition & 1 deletion axum/src/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use serde::Serialize;
/// requests and `application/x-www-form-urlencoded` encoded request bodies for other methods. It
/// supports any type that implements [`serde::Deserialize`].
///
/// Since parsing form data might require consuming the request body, the `Form` extractor must be
/// ⚠️ Since parsing form data might require consuming the request body, the `Form` extractor must be
/// *last* if there are multiple extractors in a handler. See ["the order of
/// extractors"][order-of-extractors]
///
Expand Down
10 changes: 10 additions & 0 deletions axum/src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@
//! }
//! ```
//!
//! Instead of a direct `StatusCode`, it makes sense to use intermediate error type
//! that can ultimately be converted to `Reponse`. This allows using `?` operator
//! in handlers. See those examples:
//!
//! * [`anyhow-error-response`][anyhow] for generic boxed errors
//! * [`error-handling-and-dependency-injection`][ehdi] for application-specific detailed errors
//!
//! [anyhow]:https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs
//! [ehdi]:https://github.com/tokio-rs/axum/blob/main/examples/error-handling-and-dependency-injection/src/main.rs
//!
#![doc = include_str!("../docs/debugging_handler_type_errors.md")]

#[cfg(feature = "tokio")]
Expand Down
2 changes: 1 addition & 1 deletion axum/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use serde::{de::DeserializeOwned, Serialize};
/// type.
/// - Buffering the request body fails.
///
/// Since parsing JSON requires consuming the request body, the `Json` extractor must be
/// ⚠️ Since parsing JSON requires consuming the request body, the `Json` extractor must be
/// *last* if there are multiple extractors in a handler.
/// See ["the order of extractors"][order-of-extractors]
///
Expand Down
2 changes: 1 addition & 1 deletion examples/diesel-async-postgres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ axum = { path = "../../axum" }
axum-macros = { path = "../../axum-macros" }
bb8 = "0.8"
diesel = "2"
diesel-async = { version = "0.2", features = ["postgres", "bb8"] }
diesel-async = { version = "0.3", features = ["postgres", "bb8"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
tokio = { version = "1.0", features = ["full"] }
Expand Down

0 comments on commit 67d15ac

Please sign in to comment.