Add #[derive(FromRequestParts)] (#1305)

* Add missing leading double colon

* Separate handling of last element in FromRequest derive

* FromRequestParts derive

* fix it and add lots of tests

* docs

* changelog

* Update axum-macros/src/lib.rs

Co-authored-by: Jonas Platte <[email protected]>

Co-authored-by: David Pedersen <[email protected]>
This commit is contained in:
Jonas Platte
2022-08-23 19:14:02 +00:00
committed by GitHub
co-authored by David Pedersen
parent db08419a3b
commit 7705ef6661
21 changed files with 887 additions and 172 deletions
@@ -0,0 +1,15 @@
use axum::{extract::FromRequestParts, response::Response};
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor {
body: String,
}
fn assert_from_request()
where
Extractor: FromRequestParts<(), Rejection = Response>,
{
}
fn main() {}
@@ -0,0 +1,16 @@
error[E0277]: the trait bound `String: FromRequestParts<S>` is not satisfied
--> tests/from_request/fail/parts_extracting_body.rs:6:11
|
6 | body: String,
| ^^^^^^ the trait `FromRequestParts<S>` is not implemented for `String`
|
= help: the following other types implement trait `FromRequestParts<S>`:
<() as FromRequestParts<S>>
<(T1, T2) as FromRequestParts<S>>
<(T1, T2, T3) as FromRequestParts<S>>
<(T1, T2, T3, T4) as FromRequestParts<S>>
<(T1, T2, T3, T4, T5) as FromRequestParts<S>>
<(T1, T2, T3, T4, T5, T6) as FromRequestParts<S>>
<(T1, T2, T3, T4, T5, T6, T7) as FromRequestParts<S>>
<(T1, T2, T3, T4, T5, T6, T7, T8) as FromRequestParts<S>>
and 27 others
@@ -0,0 +1,21 @@
use axum::{
extract::{FromRequestParts, Extension},
response::Response,
};
use axum_macros::FromRequestParts;
#[derive(Clone, FromRequestParts)]
#[from_request(via(Extension))]
struct Extractor {
one: i32,
two: String,
three: bool,
}
fn assert_from_request()
where
Extractor: FromRequestParts<(), Rejection = Response>,
{
}
fn main() {}
@@ -0,0 +1,12 @@
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
{
}
fn main() {}
@@ -0,0 +1,12 @@
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor();
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
{
}
fn main() {}
@@ -0,0 +1,12 @@
use axum::{body::Body, routing::get, Extension, Router};
use axum_macros::FromRequestParts;
#[derive(FromRequestParts, Clone)]
#[from_request(via(Extension))]
enum Extractor {}
async fn foo(_: Extractor) {}
fn main() {
Router::<(), Body>::new().route("/", get(foo));
}
@@ -0,0 +1,23 @@
use axum::{
extract::{rejection::TypedHeaderRejection, FromRequestParts, TypedHeader},
headers::{self, UserAgent},
response::Response,
};
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor {
uri: axum::http::Uri,
user_agent: TypedHeader<UserAgent>,
content_type: TypedHeader<headers::ContentType>,
etag: Option<TypedHeader<headers::ETag>>,
host: Result<TypedHeader<headers::Host>, TypedHeaderRejection>,
}
fn assert_from_request()
where
Extractor: FromRequestParts<(), Rejection = Response>,
{
}
fn main() {}
@@ -0,0 +1,34 @@
use axum::{
response::Response,
extract::{
rejection::TypedHeaderRejection,
Extension, FromRequestParts, TypedHeader,
},
headers::{self, UserAgent},
};
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor {
#[from_request(via(Extension))]
state: State,
#[from_request(via(TypedHeader))]
user_agent: UserAgent,
#[from_request(via(TypedHeader))]
content_type: headers::ContentType,
#[from_request(via(TypedHeader))]
etag: Option<headers::ETag>,
#[from_request(via(TypedHeader))]
host: Result<headers::Host, TypedHeaderRejection>,
}
fn assert_from_request()
where
Extractor: FromRequestParts<(), Rejection = Response>,
{
}
#[derive(Clone)]
struct State;
fn main() {}
@@ -0,0 +1,39 @@
use axum::{
extract::rejection::JsonRejection,
response::{IntoResponse, Response},
routing::get,
Router,
};
use axum_macros::FromRequest;
use std::collections::HashMap;
use serde::Deserialize;
fn main() {
let _: Router = Router::new().route("/", get(handler).post(handler_result));
}
async fn handler(_: MyJson) {}
async fn handler_result(_: Result<MyJson, MyJsonRejection>) {}
#[derive(FromRequest, Deserialize)]
#[from_request(
via(axum::extract::Json),
rejection(MyJsonRejection),
)]
#[serde(transparent)]
struct MyJson(HashMap<String, String>);
struct MyJsonRejection {}
impl From<JsonRejection> for MyJsonRejection {
fn from(_: JsonRejection) -> Self {
todo!()
}
}
impl IntoResponse for MyJsonRejection {
fn into_response(self) -> Response {
todo!()
}
}
@@ -0,0 +1,39 @@
use axum::{
extract::rejection::QueryRejection,
response::{IntoResponse, Response},
routing::get,
Router,
};
use axum_macros::FromRequestParts;
use std::collections::HashMap;
use serde::Deserialize;
fn main() {
let _: Router = Router::new().route("/", get(handler).post(handler_result));
}
async fn handler(_: MyQuery) {}
async fn handler_result(_: Result<MyQuery, MyQueryRejection>) {}
#[derive(FromRequestParts, Deserialize)]
#[from_request(
via(axum::extract::Query),
rejection(MyQueryRejection),
)]
#[serde(transparent)]
struct MyQuery(HashMap<String, String>);
struct MyQueryRejection {}
impl From<QueryRejection> for MyQueryRejection {
fn from(_: QueryRejection) -> Self {
todo!()
}
}
impl IntoResponse for MyQueryRejection {
fn into_response(self) -> Response {
todo!()
}
}
@@ -0,0 +1,61 @@
use axum::{
async_trait,
extract::{rejection::ExtensionRejection, FromRequestParts},
http::{request::Parts, StatusCode},
response::{IntoResponse, Response},
routing::get,
Extension, Router,
};
use axum_macros::FromRequestParts;
fn main() {
let _: Router = Router::new().route("/", get(handler).post(handler_result));
}
async fn handler(_: MyExtractor) {}
async fn handler_result(_: Result<MyExtractor, MyRejection>) {}
#[derive(FromRequestParts)]
#[from_request(rejection(MyRejection))]
struct MyExtractor {
one: Extension<String>,
#[from_request(via(Extension))]
two: String,
three: OtherExtractor,
}
struct OtherExtractor;
#[async_trait]
impl<S> FromRequestParts<S> for OtherExtractor
where
S: Send + Sync,
{
// this rejection doesn't implement `Display` and `Error`
type Rejection = (StatusCode, String);
async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
todo!()
}
}
struct MyRejection {}
impl From<ExtensionRejection> for MyRejection {
fn from(_: ExtensionRejection) -> Self {
todo!()
}
}
impl From<(StatusCode, String)> for MyRejection {
fn from(_: (StatusCode, String)) -> Self {
todo!()
}
}
impl IntoResponse for MyRejection {
fn into_response(self) -> Response {
todo!()
}
}
@@ -0,0 +1,33 @@
use axum::{
extract::rejection::ExtensionRejection,
response::{IntoResponse, Response},
routing::get,
Router,
};
use axum_macros::FromRequestParts;
fn main() {
let _: Router = Router::new().route("/", get(handler).post(handler_result));
}
async fn handler(_: MyExtractor) {}
async fn handler_result(_: Result<MyExtractor, MyRejection>) {}
#[derive(FromRequestParts, Clone)]
#[from_request(via(axum::Extension), rejection(MyRejection))]
enum MyExtractor {}
struct MyRejection {}
impl From<ExtensionRejection> for MyRejection {
fn from(_: ExtensionRejection) -> Self {
todo!()
}
}
impl IntoResponse for MyRejection {
fn into_response(self) -> Response {
todo!()
}
}
@@ -0,0 +1,40 @@
use axum::{
extract::rejection::QueryRejection,
response::{IntoResponse, Response},
routing::get,
Router,
};
use axum_macros::FromRequestParts;
use serde::Deserialize;
fn main() {
let _: Router = Router::new().route("/", get(handler).post(handler_result));
}
#[derive(Deserialize)]
struct Payload {}
async fn handler(_: MyQuery<Payload>) {}
async fn handler_result(_: Result<MyQuery<Payload>, MyQueryRejection>) {}
#[derive(FromRequestParts)]
#[from_request(
via(axum::extract::Query),
rejection(MyQueryRejection),
)]
struct MyQuery<T>(T);
struct MyQueryRejection {}
impl From<QueryRejection> for MyQueryRejection {
fn from(_: QueryRejection) -> Self {
todo!()
}
}
impl IntoResponse for MyQueryRejection {
fn into_response(self) -> Response {
todo!()
}
}
@@ -0,0 +1,12 @@
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor(axum::http::HeaderMap, axum::http::Method);
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<()>,
{
}
fn main() {}
@@ -0,0 +1,20 @@
use axum::extract::Query;
use axum_macros::FromRequestParts;
use serde::Deserialize;
#[derive(FromRequestParts)]
struct Extractor(
Query<Payload>,
axum::extract::Path<Payload>,
);
#[derive(Deserialize)]
struct Payload {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<()>,
{
}
fn main() {}
@@ -0,0 +1,21 @@
use axum::extract::Query;
use axum::response::Response;
use axum_macros::FromRequestParts;
use serde::Deserialize;
#[derive(FromRequestParts)]
struct Extractor(
#[from_request(via(Query))] Payload,
#[from_request(via(axum::extract::Path))] Payload,
);
#[derive(Deserialize)]
struct Payload {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<(), Rejection = Response>,
{
}
fn main() {}
@@ -0,0 +1,16 @@
use axum::Extension;
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor(#[from_request(via(Extension))] State);
#[derive(Clone)]
struct State;
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<()>,
{
}
fn main() {}
@@ -0,0 +1,12 @@
use axum_macros::FromRequestParts;
#[derive(FromRequestParts)]
struct Extractor;
fn assert_from_request()
where
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
{
}
fn main() {}