mirror of
https://github.com/tokio-rs/axum.git
synced 2025-09-30 22:41:03 +00:00
Port other proc-macros to new attribute parsing (#1372)
This commit is contained in:
parent
8da69a98fc
commit
2abda4de88
@ -1,5 +1,8 @@
|
|||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
Token,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn parse_parenthesized_attribute<K, T>(
|
pub(crate) fn parse_parenthesized_attribute<K, T>(
|
||||||
input: ParseStream,
|
input: ParseStream,
|
||||||
@ -26,6 +29,29 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_assignment_attribute<K, T>(
|
||||||
|
input: ParseStream,
|
||||||
|
out: &mut Option<(K, T)>,
|
||||||
|
) -> syn::Result<()>
|
||||||
|
where
|
||||||
|
K: Parse + ToTokens,
|
||||||
|
T: Parse,
|
||||||
|
{
|
||||||
|
let kw = input.parse()?;
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let inner = input.parse()?;
|
||||||
|
|
||||||
|
if out.is_some() {
|
||||||
|
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
|
||||||
|
let msg = format!("`{}` specified more than once", kw_name);
|
||||||
|
return Err(syn::Error::new_spanned(kw, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = Some((kw, inner));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) trait Combine: Sized {
|
pub(crate) trait Combine: Sized {
|
||||||
fn combine(self, other: Self) -> syn::Result<Self>;
|
fn combine(self, other: Self) -> syn::Result<Self>;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
use crate::with_position::{Position, WithPosition};
|
use crate::{
|
||||||
|
attr_parsing::{parse_assignment_attribute, second},
|
||||||
|
with_position::{Position, WithPosition},
|
||||||
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type};
|
use syn::{parse::Parse, parse_quote, spanned::Spanned, FnArg, ItemFn, Token, Type};
|
||||||
|
|
||||||
|
pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
|
||||||
|
let Attrs { body_ty, state_ty } = attr;
|
||||||
|
|
||||||
|
let body_ty = body_ty
|
||||||
|
.map(second)
|
||||||
|
.unwrap_or_else(|| parse_quote!(axum::body::Body));
|
||||||
|
|
||||||
|
let mut state_ty = state_ty.map(second);
|
||||||
|
|
||||||
pub(crate) fn expand(mut attr: Attrs, item_fn: ItemFn) -> TokenStream {
|
|
||||||
let check_extractor_count = check_extractor_count(&item_fn);
|
let check_extractor_count = check_extractor_count(&item_fn);
|
||||||
let check_path_extractor = check_path_extractor(&item_fn);
|
let check_path_extractor = check_path_extractor(&item_fn);
|
||||||
let check_output_impls_into_response = check_output_impls_into_response(&item_fn);
|
let check_output_impls_into_response = check_output_impls_into_response(&item_fn);
|
||||||
@ -12,14 +23,14 @@ pub(crate) fn expand(mut attr: Attrs, item_fn: ItemFn) -> TokenStream {
|
|||||||
// If the function is generic, we can't reliably check its inputs or whether the future it
|
// If the function is generic, we can't reliably check its inputs or whether the future it
|
||||||
// returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.
|
// returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.
|
||||||
let check_inputs_and_future_send = if item_fn.sig.generics.params.is_empty() {
|
let check_inputs_and_future_send = if item_fn.sig.generics.params.is_empty() {
|
||||||
if attr.state_ty.is_none() {
|
if state_ty.is_none() {
|
||||||
attr.state_ty = state_type_from_args(&item_fn);
|
state_ty = state_type_from_args(&item_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
let state_ty = attr.state_ty.unwrap_or_else(|| syn::parse_quote!(()));
|
let state_ty = state_ty.unwrap_or_else(|| syn::parse_quote!(()));
|
||||||
|
|
||||||
let check_inputs_impls_from_request =
|
let check_inputs_impls_from_request =
|
||||||
check_inputs_impls_from_request(&item_fn, &attr.body_ty, state_ty);
|
check_inputs_impls_from_request(&item_fn, &body_ty, state_ty);
|
||||||
let check_future_send = check_future_send(&item_fn);
|
let check_future_send = check_future_send(&item_fn);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
@ -49,8 +60,8 @@ mod kw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Attrs {
|
pub(crate) struct Attrs {
|
||||||
body_ty: Type,
|
body_ty: Option<(kw::body, Type)>,
|
||||||
state_ty: Option<Type>,
|
state_ty: Option<(kw::state, Type)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Attrs {
|
impl Parse for Attrs {
|
||||||
@ -60,27 +71,10 @@ impl Parse for Attrs {
|
|||||||
|
|
||||||
while !input.is_empty() {
|
while !input.is_empty() {
|
||||||
let lh = input.lookahead1();
|
let lh = input.lookahead1();
|
||||||
|
|
||||||
if lh.peek(kw::body) {
|
if lh.peek(kw::body) {
|
||||||
let kw = input.parse::<kw::body>()?;
|
parse_assignment_attribute(input, &mut body_ty)?;
|
||||||
if body_ty.is_some() {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
kw,
|
|
||||||
"`body` specified more than once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
input.parse::<Token![=]>()?;
|
|
||||||
body_ty = Some(input.parse()?);
|
|
||||||
} else if lh.peek(kw::state) {
|
} else if lh.peek(kw::state) {
|
||||||
let kw = input.parse::<kw::state>()?;
|
parse_assignment_attribute(input, &mut state_ty)?;
|
||||||
if state_ty.is_some() {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
kw,
|
|
||||||
"`state` specified more than once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
input.parse::<Token![=]>()?;
|
|
||||||
state_ty = Some(input.parse()?);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(lh.error());
|
return Err(lh.error());
|
||||||
}
|
}
|
||||||
@ -88,8 +82,6 @@ impl Parse for Attrs {
|
|||||||
let _ = input.parse::<Token![,]>();
|
let _ = input.parse::<Token![,]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_ty = body_ty.unwrap_or_else(|| syn::parse_quote!(axum::body::Body));
|
|
||||||
|
|
||||||
Ok(Self { body_ty, state_ty })
|
Ok(Self { body_ty, state_ty })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ use proc_macro2::{Span, TokenStream};
|
|||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
use syn::{parse::Parse, ItemStruct, LitStr, Token};
|
use syn::{parse::Parse, ItemStruct, LitStr, Token};
|
||||||
|
|
||||||
|
use crate::attr_parsing::{combine_attribute, parse_parenthesized_attribute, second, Combine};
|
||||||
|
|
||||||
pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result<TokenStream> {
|
pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result<TokenStream> {
|
||||||
let ItemStruct {
|
let ItemStruct {
|
||||||
attrs,
|
attrs,
|
||||||
@ -18,7 +20,16 @@ pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result<TokenStream> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let Attrs { path, rejection } = parse_attrs(attrs)?;
|
let Attrs { path, rejection } = crate::attr_parsing::parse_attrs("typed_path", attrs)?;
|
||||||
|
|
||||||
|
let path = path.ok_or_else(|| {
|
||||||
|
syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"Missing path: `#[typed_path(\"/foo/bar\")]`",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let rejection = rejection.map(second);
|
||||||
|
|
||||||
match fields {
|
match fields {
|
||||||
syn::Fields::Named(_) => {
|
syn::Fields::Named(_) => {
|
||||||
@ -37,52 +48,49 @@ mod kw {
|
|||||||
syn::custom_keyword!(rejection);
|
syn::custom_keyword!(rejection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
struct Attrs {
|
struct Attrs {
|
||||||
path: LitStr,
|
path: Option<LitStr>,
|
||||||
rejection: Option<syn::Path>,
|
rejection: Option<(kw::rejection, syn::Path)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Attrs {
|
impl Parse for Attrs {
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
let path = input.parse()?;
|
let mut path = None;
|
||||||
|
let mut rejection = None;
|
||||||
|
|
||||||
let rejection = if input.is_empty() {
|
while !input.is_empty() {
|
||||||
None
|
let lh = input.lookahead1();
|
||||||
} else {
|
if lh.peek(LitStr) {
|
||||||
let _: Token![,] = input.parse()?;
|
path = Some(input.parse()?);
|
||||||
let _: kw::rejection = input.parse()?;
|
} else if lh.peek(kw::rejection) {
|
||||||
|
parse_parenthesized_attribute(input, &mut rejection)?;
|
||||||
|
} else {
|
||||||
|
return Err(lh.error());
|
||||||
|
}
|
||||||
|
|
||||||
let content;
|
let _ = input.parse::<Token![,]>();
|
||||||
syn::parenthesized!(content in input);
|
}
|
||||||
Some(content.parse()?)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { path, rejection })
|
Ok(Self { path, rejection })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attrs> {
|
impl Combine for Attrs {
|
||||||
let mut out = None;
|
fn combine(mut self, other: Self) -> syn::Result<Self> {
|
||||||
|
let Self { path, rejection } = other;
|
||||||
for attr in attrs {
|
if let Some(path) = path {
|
||||||
if attr.path.is_ident("typed_path") {
|
if self.path.is_some() {
|
||||||
if out.is_some() {
|
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
attr,
|
path,
|
||||||
"`typed_path` specified more than once",
|
"path specified more than once",
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
out = Some(attr.parse_args()?);
|
|
||||||
}
|
}
|
||||||
|
self.path = Some(path);
|
||||||
}
|
}
|
||||||
|
combine_attribute(&mut self.rejection, rejection)?;
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
out.ok_or_else(|| {
|
|
||||||
syn::Error::new(
|
|
||||||
Span::call_site(),
|
|
||||||
"missing `#[typed_path(\"...\")]` attribute",
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_named_fields(
|
fn expand_named_fields(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user