mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-23 00:00:15 +02:00
Support State with #[derive(FromRequest[Parts])] (#1391)
* Support `State` with `#[derive(FromRequest[Parts])]` Fixes https://github.com/tokio-rs/axum/issues/1314 This makes it possible to extract things via `State` in `#[derive(FromRequet)]`: ```rust struct Foo { state: State<AppState>, } ``` The state can also be inferred in a lot of cases so you only need to write: ```rust struct Foo { // since we're using `State<AppState>` we know the state has to be // `AppState` state: State<AppState>, } ``` Same for ```rust struct Foo { #[from_request(via(State))] state: AppState, } ``` And ```rust struct AppState {} ``` I think I've covered all the edge cases but there are (unsurprisingly) a few. * make sure things can be combined with other extractors * main functions in ui tests don't need to be async * Add test for multiple identicaly state types * Add failing test for multiple states
This commit is contained in:
@@ -4,7 +4,6 @@ use crate::{
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse::Parse, parse_quote, spanned::Spanned, FnArg, ItemFn, Token, Type};
|
||||
|
||||
pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
|
||||
@@ -435,7 +434,7 @@ fn self_receiver(item_fn: &ItemFn) -> Option<TokenStream> {
|
||||
///
|
||||
/// Returns `None` if there are no `State` args or multiple of different types.
|
||||
fn state_type_from_args(item_fn: &ItemFn) -> Option<Type> {
|
||||
let state_inputs = item_fn
|
||||
let types = item_fn
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
@@ -443,44 +442,8 @@ fn state_type_from_args(item_fn: &ItemFn) -> Option<Type> {
|
||||
FnArg::Receiver(_) => None,
|
||||
FnArg::Typed(pat_type) => Some(pat_type),
|
||||
})
|
||||
.map(|pat_type| &pat_type.ty)
|
||||
.filter_map(|ty| {
|
||||
if let Type::Path(path) = &**ty {
|
||||
Some(&path.path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|path| {
|
||||
if let Some(last_segment) = path.segments.last() {
|
||||
if last_segment.ident != "State" {
|
||||
return None;
|
||||
}
|
||||
|
||||
match &last_segment.arguments {
|
||||
syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
|
||||
Some(args.args.first().unwrap())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|generic_arg| {
|
||||
if let syn::GenericArgument::Type(ty) = generic_arg {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
if state_inputs.len() == 1 {
|
||||
state_inputs.iter().next().map(|&ty| ty.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|pat_type| &*pat_type.ty);
|
||||
crate::infer_state_type(types)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user