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().name(stringify!(#name))
220                .build(ntex::rt::DefaultRuntime)
221                .block_on(async move { #body })
222        }
223    })
224    .into()
225}
226
227/// Marks async test function to be executed by ntex runtime.
228///
229/// ## Usage
230///
231/// ```no_run
232/// #[ntex::test]
233/// async fn my_test() {
234///     assert!(true);
235/// }
236/// ```
237#[proc_macro_attribute]
238pub fn rt_test(_: TokenStream, item: TokenStream) -> TokenStream {
239    let input = syn::parse_macro_input!(item as syn::ItemFn);
240
241    let ret = &input.sig.output;
242    let name = &input.sig.ident;
243    let body = &input.block;
244    let attrs = &input.attrs;
245    let mut has_test_attr = false;
246
247    for attr in attrs {
248        if attr.path.is_ident("test") {
249            has_test_attr = true;
250        }
251    }
252
253    if input.sig.asyncness.is_none() {
254        return syn::Error::new_spanned(
255            input.sig.fn_token,
256            format!("only async fn is supported, {}", input.sig.ident),
257        )
258        .to_compile_error()
259        .into();
260    }
261
262    let result = if has_test_attr {
263        quote! {
264            #(#attrs)*
265            fn #name() #ret {
266                ntex::util::enable_test_logging();
267                ntex::rt::System::build().name("test")
268                    .build(ntex::rt::DefaultRuntime)
269                    .block_on(async { #body })
270            }
271        }
272    } else {
273        quote! {
274            #[test]
275            #(#attrs)*
276            fn #name() #ret {
277                ntex::util::enable_test_logging();
278                ntex::rt::System::build().name("test")
279                    .build(ntex::rt::DefaultRuntime)
280                    .block_on(async { #body })
281            }
282        }
283    };
284
285    result.into()
286}
287
288/// Marks async test function to be executed by ntex runtime.
289///
290/// ## Usage
291///
292/// ```no_run
293/// #[ntex::test]
294/// async fn my_test() {
295///     assert!(true);
296/// }
297/// ```
298#[doc(hidden)]
299#[proc_macro_attribute]
300pub fn rt_test2(_: TokenStream, item: TokenStream) -> TokenStream {
301    let input = syn::parse_macro_input!(item as syn::ItemFn);
302
303    let ret = &input.sig.output;
304    let name = &input.sig.ident;
305    let body = &input.block;
306    let attrs = &input.attrs;
307    let mut has_test_attr = false;
308
309    for attr in attrs {
310        if attr.path.is_ident("test") {
311            has_test_attr = true;
312        }
313    }
314
315    if input.sig.asyncness.is_none() {
316        return syn::Error::new_spanned(
317            input.sig.fn_token,
318            format!("only async fn is supported, {}", input.sig.ident),
319        )
320        .to_compile_error()
321        .into();
322    }
323
324    let result = if has_test_attr {
325        quote! {
326            #(#attrs)*
327            fn #name() #ret {
328                ntex_rt::System::build().name("test")
329                    .build(ntex::rt::DefaultRuntime)
330                    .block_on(async { #body })
331            }
332        }
333    } else {
334        quote! {
335            #[test]
336            #(#attrs)*
337            fn #name() #ret {
338                ntex_rt::System::build().name("test")
339                    .build(ntex::rt::DefaultRuntime)
340                    .block_on(async { #body })
341            }
342        }
343    };
344
345    result.into()
346}
347
348/// Marks async test function to be executed by ntex runtime.
349///
350/// ## Usage
351///
352/// ```no_run
353/// #[ntex::test]
354/// async fn my_test() {
355///     assert!(true);
356/// }
357/// ```
358#[doc(hidden)]
359#[proc_macro_attribute]
360pub fn rt_test_internal(_: TokenStream, item: TokenStream) -> TokenStream {
361    let input = syn::parse_macro_input!(item as syn::ItemFn);
362
363    let ret = &input.sig.output;
364    let name = &input.sig.ident;
365    let body = &input.block;
366    let attrs = &input.attrs;
367    let mut has_test_attr = false;
368
369    for attr in attrs {
370        if attr.path.is_ident("test") {
371            has_test_attr = true;
372        }
373    }
374
375    if input.sig.asyncness.is_none() {
376        return syn::Error::new_spanned(
377            input.sig.fn_token,
378            format!("only async fn is supported, {}", input.sig.ident),
379        )
380        .to_compile_error()
381        .into();
382    }
383
384    let result = if has_test_attr {
385        quote! {
386            #(#attrs)*
387            fn #name() #ret {
388                crate::util::enable_test_logging();
389                ntex_rt::System::build().name("test")
390                    .build(crate::rt::DefaultRuntime)
391                    .block_on(async { #body })
392            }
393        }
394    } else {
395        quote! {
396            #[test]
397            #(#attrs)*
398            fn #name() #ret {
399                crate::util::enable_test_logging();
400                ntex_rt::System::build().name("test")
401                    .build(crate::rt::DefaultRuntime)
402                    .block_on(async { #body })
403            }
404        }
405    };
406
407    result.into()
408}