1use proc_macro::TokenStream;
13use quote::quote;
14use syn::{parse_macro_input, ItemFn, LitStr};
15
16#[proc_macro_attribute]
34pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
35 let input = parse_macro_input!(item as ItemFn);
36
37 let attrs = &input.attrs;
38 let vis = &input.vis;
39 let sig = &input.sig;
40 let block = &input.block;
41
42 let expanded = quote! {
43 #(#attrs)*
44 #[::tokio::main]
45 #vis #sig {
46 #block
47 }
48 };
49
50 TokenStream::from(expanded)
51}
52
53fn generate_route_handler(method: &str, attr: TokenStream, item: TokenStream) -> TokenStream {
55 let path = parse_macro_input!(attr as LitStr);
56 let input = parse_macro_input!(item as ItemFn);
57
58 let fn_name = &input.sig.ident;
59 let fn_vis = &input.vis;
60 let fn_attrs = &input.attrs;
61 let fn_async = &input.sig.asyncness;
62 let fn_inputs = &input.sig.inputs;
63 let fn_output = &input.sig.output;
64 let fn_block = &input.block;
65 let fn_generics = &input.sig.generics;
66
67 let path_value = path.value();
68
69 let route_fn_name = syn::Ident::new(
71 &format!("{}_route", fn_name),
72 fn_name.span()
73 );
74
75 let route_helper = match method {
77 "GET" => quote!(::rustapi_rs::get_route),
78 "POST" => quote!(::rustapi_rs::post_route),
79 "PUT" => quote!(::rustapi_rs::put_route),
80 "PATCH" => quote!(::rustapi_rs::patch_route),
81 "DELETE" => quote!(::rustapi_rs::delete_route),
82 _ => quote!(::rustapi_rs::get_route),
83 };
84
85 let mut chained_calls = quote!();
87
88 for attr in fn_attrs {
89 if let Some(ident) = attr.path().segments.last().map(|s| &s.ident) {
92 let ident_str = ident.to_string();
93 if ident_str == "tag" {
94 if let Ok(lit) = attr.parse_args::<LitStr>() {
95 let val = lit.value();
96 chained_calls = quote! { #chained_calls .tag(#val) };
97 }
98 } else if ident_str == "summary" {
99 if let Ok(lit) = attr.parse_args::<LitStr>() {
100 let val = lit.value();
101 chained_calls = quote! { #chained_calls .summary(#val) };
102 }
103 } else if ident_str == "description" {
104 if let Ok(lit) = attr.parse_args::<LitStr>() {
105 let val = lit.value();
106 chained_calls = quote! { #chained_calls .description(#val) };
107 }
108 }
109 }
110 }
111
112 let expanded = quote! {
113 #(#fn_attrs)*
115 #fn_vis #fn_async fn #fn_name #fn_generics (#fn_inputs) #fn_output #fn_block
116
117 #[doc(hidden)]
119 #fn_vis fn #route_fn_name() -> ::rustapi_rs::Route {
120 #route_helper(#path_value, #fn_name)
121 #chained_calls
122 }
123 };
124
125 TokenStream::from(expanded)
126}
127
128#[proc_macro_attribute]
144pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
145 generate_route_handler("GET", attr, item)
146}
147
148#[proc_macro_attribute]
150pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
151 generate_route_handler("POST", attr, item)
152}
153
154#[proc_macro_attribute]
156pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
157 generate_route_handler("PUT", attr, item)
158}
159
160#[proc_macro_attribute]
162pub fn patch(attr: TokenStream, item: TokenStream) -> TokenStream {
163 generate_route_handler("PATCH", attr, item)
164}
165
166#[proc_macro_attribute]
168pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
169 generate_route_handler("DELETE", attr, item)
170}
171
172#[proc_macro_attribute]
188pub fn tag(attr: TokenStream, item: TokenStream) -> TokenStream {
189 let tag = parse_macro_input!(attr as LitStr);
190 let input = parse_macro_input!(item as ItemFn);
191
192 let attrs = &input.attrs;
193 let vis = &input.vis;
194 let sig = &input.sig;
195 let block = &input.block;
196 let tag_value = tag.value();
197
198 let expanded = quote! {
200 #[doc = concat!("**Tag:** ", #tag_value)]
201 #(#attrs)*
202 #vis #sig #block
203 };
204
205 TokenStream::from(expanded)
206}
207
208#[proc_macro_attribute]
220pub fn summary(attr: TokenStream, item: TokenStream) -> TokenStream {
221 let summary = parse_macro_input!(attr as LitStr);
222 let input = parse_macro_input!(item as ItemFn);
223
224 let attrs = &input.attrs;
225 let vis = &input.vis;
226 let sig = &input.sig;
227 let block = &input.block;
228 let summary_value = summary.value();
229
230 let expanded = quote! {
232 #[doc = #summary_value]
233 #(#attrs)*
234 #vis #sig #block
235 };
236
237 TokenStream::from(expanded)
238}
239
240#[proc_macro_attribute]
252pub fn description(attr: TokenStream, item: TokenStream) -> TokenStream {
253 let desc = parse_macro_input!(attr as LitStr);
254 let input = parse_macro_input!(item as ItemFn);
255
256 let attrs = &input.attrs;
257 let vis = &input.vis;
258 let sig = &input.sig;
259 let block = &input.block;
260 let desc_value = desc.value();
261
262 let expanded = quote! {
264 #[doc = ""]
265 #[doc = #desc_value]
266 #(#attrs)*
267 #vis #sig #block
268 };
269
270 TokenStream::from(expanded)
271}
272