1use proc_macro::TokenStream;
4
5mod controller;
6mod from_row;
7mod injectable;
8mod jwt_guard;
9mod merge;
10mod module;
11mod openapi;
12mod routes;
13mod serializable;
14mod r#test;
15mod ws_gateway;
16
17#[proc_macro_derive(Injectable, attributes(injectable))]
18pub fn derive_injectable(input: TokenStream) -> TokenStream {
20 injectable::expand(input.into())
21 .unwrap_or_else(syn::Error::into_compile_error)
22 .into()
23}
24
25#[proc_macro_derive(Module, attributes(module, global))]
26pub fn derive_module(input: TokenStream) -> TokenStream {
28 module::expand(input.into())
29 .unwrap_or_else(syn::Error::into_compile_error)
30 .into()
31}
32
33#[proc_macro_derive(FromRow, attributes(sqlx))]
34pub fn derive_from_row(input: TokenStream) -> TokenStream {
37 from_row::expand(input.into())
38 .unwrap_or_else(syn::Error::into_compile_error)
39 .into()
40}
41
42#[proc_macro_derive(Merge)]
43pub fn derive_merge(input: TokenStream) -> TokenStream {
46 merge::expand(input.into())
47 .unwrap_or_else(syn::Error::into_compile_error)
48 .into()
49}
50
51#[proc_macro_derive(OpenApiSchema, attributes(serde, garde))]
52pub fn derive_openapi_schema(input: TokenStream) -> TokenStream {
57 openapi::expand(syn::parse_macro_input!(input as syn::DeriveInput))
58 .unwrap_or_else(syn::Error::into_compile_error)
59 .into()
60}
61
62#[proc_macro_attribute]
63pub fn jwt_guard(attribute: TokenStream, item: TokenStream) -> TokenStream {
85 jwt_guard::expand(attribute.into(), item.into())
86 .unwrap_or_else(syn::Error::into_compile_error)
87 .into()
88}
89
90#[proc_macro_attribute]
91pub fn controller(attribute: TokenStream, item: TokenStream) -> TokenStream {
93 controller::expand(attribute.into(), item.into())
94 .unwrap_or_else(syn::Error::into_compile_error)
95 .into()
96}
97
98#[proc_macro_attribute]
99pub fn routes(attribute: TokenStream, item: TokenStream) -> TokenStream {
101 routes::expand(attribute.into(), item.into())
102 .unwrap_or_else(syn::Error::into_compile_error)
103 .into()
104}
105
106#[proc_macro_derive(Serializable, attributes(exclude, expose))]
107pub fn derive_serializable(input: TokenStream) -> TokenStream {
110 serializable::expand(syn::parse_macro_input!(input as syn::DeriveInput))
111 .unwrap_or_else(syn::Error::into_compile_error)
112 .into()
113}
114
115macro_rules! marker_attribute {
116 ($($name:ident),+ $(,)?) => {$ (
117 #[doc = concat!("Route metadata consumed by [`macro@routes`].")]
118 #[proc_macro_attribute]
119 pub fn $name(_attribute: TokenStream, item: TokenStream) -> TokenStream {
120 item
121 }
122 )+};
123}
124
125#[proc_macro_attribute]
126pub fn web_socket_gateway(attribute: TokenStream, item: TokenStream) -> TokenStream {
128 ws_gateway::expand(attribute.into(), item.into())
129 .unwrap_or_else(syn::Error::into_compile_error)
130 .into()
131}
132
133marker_attribute!(
134 get,
135 post,
136 put,
137 patch,
138 delete,
139 head,
140 options,
141 body,
142 form,
143 query,
144 param,
145 header,
146 decorator,
147 pipe,
148 subscribe_message,
149 guard,
150 interceptor,
151 middleware,
152 cache,
153 cron,
154 interval,
155 timeout,
156 api,
157 resp,
158);
159
160#[proc_macro_attribute]
174pub fn r#test(attribute: TokenStream, item: TokenStream) -> TokenStream {
175 r#test::expand(attribute.into(), item.into())
176 .unwrap_or_else(syn::Error::into_compile_error)
177 .into()
178}
179
180#[proc_macro_attribute]
182pub fn main(attribute: TokenStream, item: TokenStream) -> TokenStream {
183 if !attribute.is_empty() {
184 return syn::Error::new(
185 proc_macro2::Span::call_site(),
186 "`#[ironic::main]` does not accept arguments",
187 )
188 .into_compile_error()
189 .into();
190 }
191 let mut function = syn::parse_macro_input!(item as syn::ItemFn);
192 if function.sig.asyncness.is_none() {
193 let error = syn::Error::new_spanned(
194 function.sig.fn_token,
195 "`#[ironic::main]` requires an async function",
196 )
197 .into_compile_error();
198 return quote::quote!(#error #function).into();
199 }
200 if !function.sig.inputs.is_empty() {
201 let error = syn::Error::new_spanned(
202 &function.sig.inputs,
203 "`#[ironic::main]` entry points cannot accept arguments",
204 )
205 .into_compile_error();
206 return quote::quote!(#error #function).into();
207 }
208 function.sig.asyncness = None;
209 let body = function.block;
210 function.block = Box::new(syn::parse_quote!({
211 ::ironic::__private::block_on(async move #body)
212 }));
213 quote::quote!(#function).into()
214}