1use proc_macro::{TokenStream, Span};
2use syn::{parse_macro_input, ItemFn};
3use syn::punctuated::Punctuated;
4use syn::parse::{Parse, ParseStream};
5use quote::quote;
6
7struct Args {
8 vars: Vec<syn::Expr>
9}
10
11impl Parse for Args {
12 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
13 let vars = Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated(input)?;
14
15 Ok(Args {
16 vars: vars.into_iter().collect(),
17 })
18 }
19}
20
21impl Args {
22 pub fn get_method(&self) -> syn::Result<syn::Expr> {
23 match self.vars.get(0) {
24 Some(var) => Ok(var.clone()),
25 None => return Err(syn::Error::new(
26 Span::call_site().into(),
27 "No HTTP Method was provided"
28 ))
29 }
30 }
31
32 pub fn get_route(&self) -> syn::Result<syn::Expr> {
33 match self.vars.get(1) {
34 Some(var) => Ok(var.clone()),
35 None => return Err(syn::Error::new(
36 Span::call_site().into(),
37 "No Route was provided"
38 ))
39 }
40 }
41}
42
43#[proc_macro_attribute]
44pub fn route(args: TokenStream, input: TokenStream) -> TokenStream {
45 let args = parse_macro_input!(args as Args);
46 let func = parse_macro_input!(input as ItemFn);
47
48 let vis = func.vis.clone();
49 let ident = func.sig.ident.clone();
50
51 let method = args.get_method().unwrap();
52 let route = args.get_route().unwrap();
53
54 let expanded = quote! {
55 #[allow(non_camel_case_types)]
56 #vis struct #ident;
57
58 impl #ident {
59 #vis fn route() -> axum::Router {
60 #func
61
62 axum::Router::new().route(#route, #method (#ident))
63 }
64 }
65 };
66
67 expanded.into()
68}