Skip to content

Commit f9654cb

Browse files
committed
add typed_method. missing comments
1 parent 4028d0e commit f9654cb

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

axum-extra/src/routing/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ mod resource;
1515
#[cfg(feature = "typed-routing")]
1616
mod typed;
1717

18+
#[cfg(feature = "typed-routing")]
19+
use crate::routing::typed::TypedMethod;
20+
1821
pub use self::resource::Resource;
1922

2023
#[cfg(feature = "typed-routing")]
@@ -79,6 +82,14 @@ macro_rules! vpath {
7982
/// Extension trait that adds additional methods to [`Router`].
8083
#[allow(clippy::return_self_not_must_use)]
8184
pub trait RouterExt<S>: sealed::Sealed {
85+
// TODO: comments
86+
#[cfg(feature = "typed-routing")]
87+
fn typed<H, T, P>(self, handler: H) -> Self
88+
where
89+
H: axum::handler::Handler<T, S>,
90+
T: SecondElementIs<P> + 'static,
91+
P: TypedMethod;
92+
8293
/// Add a typed `GET` route to the router.
8394
///
8495
/// The path will be inferred from the first argument to the handler function which must
@@ -240,6 +251,16 @@ impl<S> RouterExt<S> for Router<S>
240251
where
241252
S: Clone + Send + Sync + 'static,
242253
{
254+
#[cfg(feature = "typed-routing")]
255+
fn typed<H, T, P>(self, handler: H) -> Self
256+
where
257+
H: axum::handler::Handler<T, S>,
258+
T: SecondElementIs<P> + 'static,
259+
P: TypedMethod,
260+
{
261+
self.route(P::PATH, axum::routing::on(P::METHOD, handler))
262+
}
263+
243264
#[cfg(feature = "typed-routing")]
244265
fn typed_get<H, T, P>(self, handler: H) -> Self
245266
where

axum-extra/src/routing/typed.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ use super::sealed::Sealed;
44
use http::Uri;
55
use serde_core::Serialize;
66

7+
// TODO: comments
8+
pub trait TypedMethod: TypedPath {
9+
const METHOD: axum::routing::MethodFilter;
10+
}
11+
712
/// A type safe path.
813
///
914
/// This is used to statically connect a path to its corresponding handler using

axum-macros/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod axum_test;
1717
mod debug_handler;
1818
mod from_ref;
1919
mod from_request;
20+
mod typed_method;
2021
mod typed_path;
2122
mod with_position;
2223

@@ -655,6 +656,16 @@ pub fn __private_axum_test(_attr: TokenStream, input: TokenStream) -> TokenStrea
655656
expand_attr_with(_attr, input, axum_test::expand)
656657
}
657658

659+
/// Derive an implementation of [`axum_extra::routing::TypedMethod`].
660+
///
661+
/// See that trait for more details.
662+
///
663+
/// [`axum_extra::routing::TypedMethod`]: https://docs.rs/axum-extra/latest/axum_extra/routing/trait.TypedMethod.html
664+
#[proc_macro_derive(TypedMethod, attributes(typed_method))]
665+
pub fn derive_typed_method(input: TokenStream) -> TokenStream {
666+
expand_with(input, |item_struct| typed_method::expand(&item_struct))
667+
}
668+
658669
/// Derive an implementation of [`axum_extra::routing::TypedPath`].
659670
///
660671
/// See that trait for more details.

axum-macros/src/typed_method.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use proc_macro2::{Span, TokenStream};
2+
use quote::{quote, quote_spanned};
3+
use syn::{parse::Parse, spanned::Spanned, ItemStruct};
4+
5+
use super::attr_parsing::Combine;
6+
7+
pub(crate) fn expand(item_struct: &ItemStruct) -> syn::Result<TokenStream> {
8+
let ItemStruct {
9+
attrs,
10+
ident,
11+
generics,
12+
..
13+
} = &item_struct;
14+
15+
if !generics.params.is_empty() || generics.where_clause.is_some() {
16+
return Err(syn::Error::new_spanned(
17+
generics,
18+
"`#[derive(TypedMethod)]` doesn't support generics",
19+
));
20+
}
21+
22+
let Attrs { method_filter } = super::attr_parsing::parse_attrs("typed_method", attrs)?;
23+
24+
let method_filter = method_filter.ok_or_else(|| {
25+
syn::Error::new(
26+
Span::call_site(),
27+
"Missing method filter: `#[typed_method(\"GET\")]`",
28+
)
29+
})?;
30+
31+
let typed_path_impl = quote_spanned! {method_filter.span()=>
32+
#[automatically_derived]
33+
impl ::axum_typed_method::TypedMethod for #ident {
34+
const METHOD: ::axum::routing::MethodFilter = #method_filter;
35+
}
36+
};
37+
38+
Ok(quote! (#typed_path_impl))
39+
}
40+
41+
#[derive(Default)]
42+
struct Attrs {
43+
method_filter: Option<syn::Path>,
44+
}
45+
46+
impl Parse for Attrs {
47+
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
48+
Ok(Self {
49+
method_filter: Some(input.parse()?),
50+
})
51+
}
52+
}
53+
54+
impl Combine for Attrs {
55+
fn combine(mut self, other: Self) -> syn::Result<Self> {
56+
let Self { method_filter } = other;
57+
if let Some(method_filter) = method_filter {
58+
if self.method_filter.is_some() {
59+
return Err(syn::Error::new_spanned(
60+
method_filter,
61+
"method filter specified more than once",
62+
));
63+
}
64+
self.method_filter = Some(method_filter);
65+
}
66+
67+
Ok(self)
68+
}
69+
}
70+
71+
#[test]
72+
fn ui() {
73+
crate::run_ui_tests("typed_method");
74+
}

0 commit comments

Comments
 (0)