Skip to main content

ntex_macros/
lib.rs

1//! ntex macros module
2//!
3//! Generators for routes
4//!
5//! ## Route
6//!
7//! Macros:
8//!
9//! - [get](attr.web_get.html)
10//! - [post](attr.web_post.html)
11//! - [put](attr.web_put.html)
12//! - [delete](attr.web_delete.html)
13//! - [head](attr.web_head.html)
14//! - [connect](attr.web_connect.html)
15//! - [options](attr.web_options.html)
16//! - [trace](attr.web_trace.html)
17//! - [patch](attr.web_patch.html)
18//!
19//! ### Attributes:
20//!
21//! - `"path"` - Raw literal string with path for which to register handle. Mandatory.
22//! - `guard = "function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
23//! - `error = "ErrorRenderer"` - Register handler for specified error renderer
24//!
25//! ## Notes
26//!
27//! Function name can be specified as any expression that is going to be accessible to the generate
28//! code (e.g `my_guard` or `my_module::my_guard`)
29//!
30//! ## Example:
31//!
32//! ```rust
33//! use ntex::web::{get, Error, HttpResponse};
34//! use futures::{future, Future};
35//!
36//! #[get("/test")]
37//! async fn async_test() -> Result<HttpResponse, Error> {
38//!     Ok(HttpResponse::Ok().finish())
39//! }
40//! ```
41
42extern crate proc_macro;
43
44mod route;
45
46use proc_macro::TokenStream;
47use quote::quote;
48use syn::parse_macro_input;
49
50/// Creates route handler with `GET` method guard.
51///
52/// Syntax: `#[get("path"[, attributes])]`
53///
54/// ## Attributes:
55///
56/// - `"path"` - Raw literal string with path for which to register handler. Mandatory.
57/// - `guard = "function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
58/// - `error = "ErrorRenderer"` - Register handler for different error renderer
59#[proc_macro_attribute]
60pub fn web_get(args: TokenStream, input: TokenStream) -> TokenStream {
61    let args = parse_macro_input!(args as syn::AttributeArgs);
62    let gen_code = match route::Route::new(args, input, route::MethodType::Get) {
63        Ok(gen_code) => gen_code,
64        Err(err) => return err.to_compile_error().into(),
65    };
66    gen_code.generate()
67}
68
69/// Creates route handler with `POST` method guard.
70///
71/// Syntax: `#[post("path"[, attributes])]`
72///
73/// Attributes are the same as in [get](attr.get.html)
74#[proc_macro_attribute]
75pub fn web_post(args: TokenStream, input: TokenStream) -> TokenStream {
76    let args = parse_macro_input!(args as syn::AttributeArgs);
77    let gen_code = match route::Route::new(args, input, route::MethodType::Post) {
78        Ok(gen_code) => gen_code,
79        Err(err) => return err.to_compile_error().into(),
80    };
81    gen_code.generate()
82}
83
84/// Creates route handler with `PUT` method guard.
85///
86/// Syntax: `#[put("path"[, attributes])]`
87///
88/// Attributes are the same as in [get](attr.get.html)
89#[proc_macro_attribute]
90pub fn web_put(args: TokenStream, input: TokenStream) -> TokenStream {
91    let args = parse_macro_input!(args as syn::AttributeArgs);
92    let gen_code = match route::Route::new(args, input, route::MethodType::Put) {
93        Ok(gen_code) => gen_code,
94        Err(err) => return err.to_compile_error().into(),
95    };
96    gen_code.generate()
97}
98
99/// Creates route handler with `DELETE` method guard.
100///
101/// Syntax: `#[delete("path"[, attributes])]`
102///
103/// Attributes are the same as in [get](attr.get.html)
104#[proc_macro_attribute]
105pub fn web_delete(args: TokenStream, input: TokenStream) -> TokenStream {
106    let args = parse_macro_input!(args as syn::AttributeArgs);
107    let gen_code = match route::Route::new(args, input, route::MethodType::Delete) {
108        Ok(gen_code) => gen_code,
109        Err(err) => return err.to_compile_error().into(),
110    };
111    gen_code.generate()
112}
113
114/// Creates route handler with `HEAD` method guard.
115///
116/// Syntax: `#[head("path"[, attributes])]`
117///
118/// Attributes are the same as in [head](attr.head.html)
119#[proc_macro_attribute]
120pub fn web_head(args: TokenStream, input: TokenStream) -> TokenStream {
121    let args = parse_macro_input!(args as syn::AttributeArgs);
122    let gen_code = match route::Route::new(args, input, route::MethodType::Head) {
123        Ok(gen_code) => gen_code,
124        Err(err) => return err.to_compile_error().into(),
125    };
126    gen_code.generate()
127}
128
129/// Creates route handler with `CONNECT` method guard.
130///
131/// Syntax: `#[connect("path"[, attributes])]`
132///
133/// Attributes are the same as in [connect](attr.connect.html)
134#[proc_macro_attribute]
135pub fn web_connect(args: TokenStream, input: TokenStream) -> TokenStream {
136    let args = parse_macro_input!(args as syn::AttributeArgs);
137    let gen_code = match route::Route::new(args, input, route::MethodType::Connect) {
138        Ok(gen_code) => gen_code,
139        Err(err) => return err.to_compile_error().into(),
140    };
141    gen_code.generate()
142}
143
144/// Creates route handler with `OPTIONS` method guard.
145///
146/// Syntax: `#[options("path"[, attributes])]`
147///
148/// Attributes are the same as in [options](attr.options.html)
149#[proc_macro_attribute]
150pub fn web_options(args: TokenStream, input: TokenStream) -> TokenStream {
151    let args = parse_macro_input!(args as syn::AttributeArgs);
152    let gen_code = match route::Route::new(args, input, route::MethodType::Options) {
153        Ok(gen_code) => gen_code,
154        Err(err) => return err.to_compile_error().into(),
155    };
156    gen_code.generate()
157}
158
159/// Creates route handler with `TRACE` method guard.
160///
161/// Syntax: `#[trace("path"[, attributes])]`
162///
163/// Attributes are the same as in [trace](attr.trace.html)
164#[proc_macro_attribute]
165pub fn web_trace(args: TokenStream, input: TokenStream) -> TokenStream {
166    let args = parse_macro_input!(args as syn::AttributeArgs);
167    let gen_code = match route::Route::new(args, input, route::MethodType::Trace) {
168        Ok(gen_code) => gen_code,
169        Err(err) => return err.to_compile_error().into(),
170    };
171    gen_code.generate()
172}
173
174/// Creates route handler with `PATCH` method guard.
175///
176/// Syntax: `#[patch("path"[, attributes])]`
177///
178/// Attributes are the same as in [patch](attr.patch.html)
179#[proc_macro_attribute]
180pub fn web_patch(args: TokenStream, input: TokenStream) -> TokenStream {
181    let args = parse_macro_input!(args as syn::AttributeArgs);
182    let gen_code = match route::Route::new(args, input, route::MethodType::Patch) {
183        Ok(gen_code) => gen_code,
184        Err(err) => return err.to_compile_error().into(),
185    };
186    gen_code.generate()
187}
188
189/// Marks async function to be executed by ntex system.
190///
191/// ## Usage
192///
193/// ```rust
194/// #[ntex::main]
195/// async fn main() {
196///     println!("Hello world");
197/// }
198/// ```
199#[proc_macro_attribute]
200pub fn rt_main(_: TokenStream, item: TokenStream) -> TokenStream {
201    let mut input = syn::parse_macro_input!(item as syn::ItemFn);
202    let attrs = &input.attrs;
203    let vis = &input.vis;
204    let sig = &mut input.sig;
205    let body = &input.block;
206    let name = &sig.ident;
207
208    if sig.asyncness.is_none() {
209        return syn::Error::new_spanned(sig.fn_token, "only async fn is supported")
210            .to_compile_error()
211            .into();
212    }
213
214    sig.asyncness = None;
215
216    (quote! {
217        #(#attrs)*
218        #vis #sig {
219            ntex::rt::System::build()
220                .name(stringify!(#name))
221                .build(ntex::rt::DefaultRuntime)
222                .block_on(async move { #body })
223        }
224    })
225    .into()
226}
227
228/// Marks async test function to be executed by ntex runtime.
229///
230/// ## Usage
231///
232/// ```no_run
233/// #[ntex::test]
234/// async fn my_test() {
235///     assert!(true);
236/// }
237/// ```
238#[proc_macro_attribute]
239pub fn rt_test(_: TokenStream, item: TokenStream) -> TokenStream {
240    let input = syn::parse_macro_input!(item as syn::ItemFn);
241
242    let ret = &input.sig.output;
243    let name = &input.sig.ident;
244    let body = &input.block;
245    let attrs = &input.attrs;
246    let mut has_test_attr = false;
247
248    for attr in attrs {
249        if attr.path.is_ident("test") {
250            has_test_attr = true;
251        }
252    }
253
254    if input.sig.asyncness.is_none() {
255        return syn::Error::new_spanned(
256            input.sig.fn_token,
257            format!("only async fn is supported, {}", input.sig.ident),
258        )
259        .to_compile_error()
260        .into();
261    }
262
263    let result = if has_test_attr {
264        quote! {
265            #(#attrs)*
266            fn #name() #ret {
267                ntex::util::enable_test_logging();
268                ntex::rt::System::build()
269                    .name(stringify!(#name))
270                    .testing()
271                    .build(ntex::rt::DefaultRuntime)
272                    .block_on(async { #body })
273            }
274        }
275    } else {
276        quote! {
277            #[test]
278            #(#attrs)*
279            fn #name() #ret {
280                ntex::util::enable_test_logging();
281                ntex::rt::System::build()
282                    .name(stringify!(#name))
283                    .testing()
284                    .build(ntex::rt::DefaultRuntime)
285                    .block_on(async { #body })
286            }
287        }
288    };
289
290    result.into()
291}
292
293/// Marks async test function to be executed by ntex runtime.
294///
295/// ## Usage
296///
297/// ```no_run
298/// #[ntex::test]
299/// async fn my_test() {
300///     assert!(true);
301/// }
302/// ```
303#[doc(hidden)]
304#[proc_macro_attribute]
305pub fn rt_test2(_: TokenStream, item: TokenStream) -> TokenStream {
306    let input = syn::parse_macro_input!(item as syn::ItemFn);
307
308    let ret = &input.sig.output;
309    let name = &input.sig.ident;
310    let body = &input.block;
311    let attrs = &input.attrs;
312    let mut has_test_attr = false;
313
314    for attr in attrs {
315        if attr.path.is_ident("test") {
316            has_test_attr = true;
317        }
318    }
319
320    if input.sig.asyncness.is_none() {
321        return syn::Error::new_spanned(
322            input.sig.fn_token,
323            format!("only async fn is supported, {}", input.sig.ident),
324        )
325        .to_compile_error()
326        .into();
327    }
328
329    let result = if has_test_attr {
330        quote! {
331            #(#attrs)*
332            fn #name() #ret {
333                ntex_rt::System::build()
334                    .name(stringify!(#name))
335                    .testing()
336                    .build(ntex::rt::DefaultRuntime)
337                    .block_on(async { #body })
338            }
339        }
340    } else {
341        quote! {
342            #[test]
343            #(#attrs)*
344            fn #name() #ret {
345                ntex_rt::System::build()
346                    .name(stringify!(#name))
347                    .testing()
348                    .build(ntex::rt::DefaultRuntime)
349                    .block_on(async { #body })
350            }
351        }
352    };
353
354    result.into()
355}
356
357/// Marks async test function to be executed by ntex runtime.
358///
359/// ## Usage
360///
361/// ```no_run
362/// #[ntex::test]
363/// async fn my_test() {
364///     assert!(true);
365/// }
366/// ```
367#[doc(hidden)]
368#[proc_macro_attribute]
369pub fn rt_test_internal(_: TokenStream, item: TokenStream) -> TokenStream {
370    let input = syn::parse_macro_input!(item as syn::ItemFn);
371
372    let ret = &input.sig.output;
373    let name = &input.sig.ident;
374    let body = &input.block;
375    let attrs = &input.attrs;
376    let mut has_test_attr = false;
377
378    for attr in attrs {
379        if attr.path.is_ident("test") {
380            has_test_attr = true;
381        }
382    }
383
384    if input.sig.asyncness.is_none() {
385        return syn::Error::new_spanned(
386            input.sig.fn_token,
387            format!("only async fn is supported, {}", input.sig.ident),
388        )
389        .to_compile_error()
390        .into();
391    }
392
393    let result = if has_test_attr {
394        quote! {
395            #(#attrs)*
396            fn #name() #ret {
397                crate::util::enable_test_logging();
398                ntex_rt::System::build()
399                    .name(stringify!(#name))
400                    .testing()
401                    .build(crate::rt::DefaultRuntime)
402                    .block_on(async { #body })
403            }
404        }
405    } else {
406        quote! {
407            #[test]
408            #(#attrs)*
409            fn #name() #ret {
410                crate::util::enable_test_logging();
411                ntex_rt::System::build()
412                    .name(stringify!(#name))
413                    .testing()
414                    .build(crate::rt::DefaultRuntime)
415                    .block_on(async { #body })
416            }
417        }
418    };
419
420    result.into()
421}