sword_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, parse_quote};
4
5mod core {
6    pub mod config;
7}
8
9mod http {
10    pub mod controller {
11        pub mod expand;
12        pub mod routes;
13
14        pub use expand::expand_controller;
15        pub use routes::expand_controller_routes;
16    }
17
18    pub mod middleware;
19    pub mod utils;
20}
21
22/// Defines a handler for HTTP GET requests.
23/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
24///
25/// ### Parameters
26/// - `path`: The path for the GET request, e.g., `"/items"`
27///
28/// ### Usage
29/// ```rust,ignore
30/// #[controller("/api")]
31/// struct MyController {}
32///
33/// #[routes]
34/// impl MyController {
35///     #[get("/items")]
36///     async fn get_items(ctx: Context) -> HttpResult<HttpResponse> {
37///         Ok(HttpResponse::Ok().message("List of items"))
38///     }
39/// }
40/// ```
41#[proc_macro_attribute]
42pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
43    let _ = attr;
44    item
45}
46
47/// Defines a handler for HTTP POST requests.
48/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
49///
50/// ### Parameters
51/// - `path`: The path for the POST request, e.g., `"/api"`
52///
53/// ## Usage
54/// ```rust,ignore
55/// #[controller("/api")]
56/// struct MyController {}
57///
58/// #[routes]
59/// impl MyController {
60///     #[post("/items")]
61///     async fn create_item(ctx: Context) -> HttpResult<HttpResponse> {
62///         Ok(HttpResponse::Ok().message("Item created"))
63///     }
64/// }
65/// ```
66#[proc_macro_attribute]
67pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
68    let _ = attr;
69    item
70}
71
72/// Defines a handler for HTTP PUT requests.
73/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
74///
75/// ### Parameters
76/// - `path`: The path for the PUT request, e.g., `"/items"`
77///
78/// ## Usage
79/// ```rust,ignore
80/// #[controller("/api")]
81/// struct MyController {}
82///
83/// #[routes]
84/// impl MyController {
85///     #[put("/item/{id}")]
86///     async fn update_item(ctx: Context) -> HttpResult<HttpResponse> {
87///         Ok(HttpResponse::Ok().message("Item updated"))
88///     }
89/// }
90/// ```
91#[proc_macro_attribute]
92pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
93    let _ = attr;
94    item
95}
96
97/// Defines a handler for HTTP DELETE requests.
98/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
99///
100/// ### Parameters
101/// - `path`: The path for the DELETE request, e.g., `"/item/{id}"`
102///
103/// ## Usage
104/// ```rust,ignore
105/// #[controller("/api")]
106/// struct MyController {}
107///
108/// #[routes]
109/// impl MyController {
110///     #[delete("/item/{id}")]
111///     async fn delete_item(ctx: Context) -> HttpResult<HttpResponse> {
112///         Ok(HttpResponse::Ok().message("Item deleted"))
113///     }
114/// }
115/// ```
116#[proc_macro_attribute]
117pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
118    let _ = attr;
119    item
120}
121
122/// Defines a handler for PATCH DELETE requests.
123/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
124///
125/// ### Parameters
126/// - `path`: The path for the PATCH request, e.g., `"/item/{id}"`
127///
128/// ## Usage
129/// ```rust,ignore
130/// #[controller("/api")]
131/// struct MyController {}
132///
133/// #[routes]
134/// impl MyController {
135///     #[patch("/item/{id}")]
136///     async fn patch_item(ctx: Context) -> HttpResult<HttpResponse> {
137///         Ok(HttpResponse::Ok().message("Item patched"))
138///     }
139/// }
140/// ```
141#[proc_macro_attribute]
142pub fn patch(attr: TokenStream, item: TokenStream) -> TokenStream {
143    let _ = attr;
144    item
145}
146
147/// Defines a controller with a base path.
148/// This macro should be used in combination with the `#[routes]` macro.
149///
150/// ### Parameters
151/// - `base_path`: The base path for the controller, e.g., `"/api
152///
153/// ### Usage
154/// ```rust,ignore
155/// #[controller("/base_path")]
156/// struct MyController {}
157///
158/// #[routes]
159/// impl MyController {
160///     #[get("/sub_path")]
161///     async fn my_handler(ctx: Context) -> HttpResult<HttpResponse> {
162///        Ok(HttpResponse::Ok().message("Hello from MyController"))    
163///     }
164/// }
165#[proc_macro_attribute]
166#[proc_macro_error::proc_macro_error]
167pub fn controller(attr: TokenStream, item: TokenStream) -> TokenStream {
168    http::controller::expand_controller(attr, item)
169}
170
171/// Implements the routes for a controller defined with the `#[controller]` macro.
172///
173/// ### Usage
174/// ```rust,ignore
175/// #[controller("/base_path")]
176/// struct MyController {}
177///
178/// #[routes]
179/// impl MyController {
180///     #[get("/sub_path")]
181///     async fn my_handler(ctx: Context) -> HttpResult<HttpResponse> {
182///        Ok(HttpResponse::Ok().message("Hello from MyController"))    
183///     }
184/// }
185/// ```
186#[proc_macro_attribute]
187#[proc_macro_error::proc_macro_error]
188pub fn routes(attr: TokenStream, item: TokenStream) -> TokenStream {
189    http::controller::expand_controller_routes(attr, item)
190}
191
192/// Declares a executable middleware to apply to a route controller.
193/// This macro should be used inside an `impl` block of a struct annotated with the `#[controller]` macro.
194///
195/// ### Parameters
196/// - `MiddlewareName`: The name of the middleware struct that implements the `Middleware` or `MiddlewareWithConfig` trait.
197///   Also can receive an instance of a `tower-http` service layer like `CorsLayer`, `CompressionLayer`, `TraceLayer`, etc.
198///   If the layer can be added without errors on Application::with_layer() there will not be any problem using it.  
199///
200/// - `config`: (Optional) Configuration parameters for the middleware,
201///
202/// ### Handle errors
203/// To throw an error from a middleware, simply return an `Err` with an `HttpResponse`
204/// struct in the same way as a controller handler.
205///
206/// ### Usage
207/// ```rust,ignore
208/// pub struct RoleMiddleware;
209///
210/// impl MiddlewareWithConfig<Vec<&str>> for RoleMiddleware {
211///     async fn handle(roles: Vec<&str>, ctx: Context, next: Next) -> MiddlewareResult {
212///         next!(ctx, next)
213///     }
214/// }
215///
216/// #[controller("/api")]
217/// struct MyController {}
218///
219/// #[routes]
220/// impl MyController {
221///     #[get("/items")]
222///     #[middleware(RoleMiddleware, config = vec!["admin", "user"])]
223///     async fn get_items(ctx: Context) -> HttpResult<HttpResponse> {
224///         Ok(HttpResponse::Ok().message("List of items"))
225///     }
226/// }
227/// ```
228#[proc_macro_attribute]
229pub fn middleware(attr: TokenStream, item: TokenStream) -> TokenStream {
230    let _ = attr;
231    item
232}
233/// Defines a configuration struct for the application.
234/// This macro generates the necessary code to deserialize the struct from
235/// the configuration toml file.
236///
237/// The struct must derive `Deserialize` from `serde`.
238///
239/// ### Parameters
240/// - `key`: The key in the configuration file where the struct is located.
241///
242/// ### Usage
243/// ```rust, ignore
244/// #[derive(Deserialize)]
245/// #[config(key = "my-section")]
246/// struct MyConfig {
247///     my_key: String,
248/// }
249/// ```
250///
251/// This allows you to access the configuration in your handlers or middlewares
252///
253/// ```rust, ignore
254/// #[controller("/some_path")]
255/// struct SomeController {}
256///
257/// #[routes]
258/// impl SomeController {
259///     #[get("/config")]
260///     async fn get_config(ctx: Context) -> HttpResult<HttpResponse> {
261///         let config = ctx.config::<MyConfig>()?;
262///         let message = format!("Config key: {}", config.my_key);
263///
264///         Ok(HttpResponse::Ok().message(message))
265///     }
266/// }
267#[proc_macro_attribute]
268pub fn config(attr: TokenStream, item: TokenStream) -> TokenStream {
269    core::config::expand_config_struct(attr, item)
270}
271
272/// ### This is just a re-export of `tokio::main` to simplify the initial setup of
273/// ### Sword, you can use your own version of tokio adding it to your
274/// ### `Cargo.toml`, we are providing this initial base by default
275///
276/// ---
277///
278/// Marks async function to be executed by the selected runtime. This macro
279/// helps set up a `Runtime` without requiring the user to use
280/// [Runtime](../tokio/runtime/struct.Runtime.html) or
281/// [Builder](../tokio/runtime/struct.Builder.html) directly.
282///
283/// Note: This macro is designed to be simplistic and targets applications that
284/// do not require a complex setup. If the provided functionality is not
285/// sufficient, you may be interested in using
286/// [Builder](../tokio/runtime/struct.Builder.html), which provides a more
287/// powerful interface.
288///
289/// Note: This macro can be used on any function and not just the `main`
290/// function. Using it on a non-main function makes the function behave as if it
291/// was synchronous by starting a new runtime each time it is called. If the
292/// function is called often, it is preferable to create the runtime using the
293/// runtime builder so the runtime can be reused across calls.
294///
295/// # Non-worker async function
296///
297/// Note that the async function marked with this macro does not run as a
298/// worker. The expectation is that other tasks are spawned by the function here.
299/// Awaiting on other futures from the function provided here will not
300/// perform as fast as those spawned as workers.
301///
302/// # Multi-threaded runtime
303///
304/// To use the multi-threaded runtime, the macro can be configured using
305///
306/// ```rust,ignore
307/// #[tokio::main(flavor = "multi_thread", worker_threads = 10)]
308/// # async fn main() {}
309/// ```
310///
311/// The `worker_threads` option configures the number of worker threads, and
312/// defaults to the number of cpus on the system. This is the default flavor.
313///
314/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
315/// flag.
316///
317/// # Current thread runtime
318///
319/// To use the single-threaded runtime known as the `current_thread` runtime,
320/// the macro can be configured using
321///
322/// ```rust,ignore
323/// #[tokio::main(flavor = "current_thread")]
324/// # async fn main() {}
325/// ```
326///
327/// ## Function arguments:
328///
329/// Arguments are allowed for any functions aside from `main` which is special
330///
331/// ## Usage
332///
333/// ### Using the multi-thread runtime
334///
335/// ```ignore
336/// #[tokio::main]
337/// async fn main() {
338///     println!("Hello world");
339/// }
340/// ```
341///
342/// Equivalent code not using `#[tokio::main]`
343///
344/// ```ignore
345/// fn main() {
346///     tokio::runtime::Builder::new_multi_thread()
347///         .enable_all()
348///         .build()
349///         .unwrap()
350///         .block_on(async {
351///             println!("Hello world");
352///         })
353/// }
354/// ```
355///
356/// ### Using current thread runtime
357///
358/// The basic scheduler is single-threaded.
359///
360/// ```ignore
361/// #[tokio::main(flavor = "current_thread")]
362/// async fn main() {
363///     println!("Hello world");
364/// }
365/// ```
366///
367/// Equivalent code not using `#[tokio::main]`
368///
369/// ```ignore
370/// fn main() {
371///     tokio::runtime::Builder::new_current_thread()
372///         .enable_all()
373///         .build()
374///         .unwrap()
375///         .block_on(async {
376///             println!("Hello world");
377///         })
378/// }
379/// ```
380///
381/// ### Set number of worker threads
382///
383/// ```ignore
384/// #[tokio::main(worker_threads = 2)]
385/// async fn main() {
386///     println!("Hello world");
387/// }
388/// ```
389///
390/// Equivalent code not using `#[tokio::main]`
391///
392/// ```ignore
393/// fn main() {
394///     tokio::runtime::Builder::new_multi_thread()
395///         .worker_threads(2)
396///         .enable_all()
397///         .build()
398///         .unwrap()
399///         .block_on(async {
400///             println!("Hello world");
401///         })
402/// }
403/// ```
404///
405/// ### Configure the runtime to start with time paused
406///
407/// ```ignore
408/// #[tokio::main(flavor = "current_thread", start_paused = true)]
409/// async fn main() {
410///     println!("Hello world");
411/// }
412/// ```
413///
414/// Equivalent code not using `#[tokio::main]`
415///
416/// ```ignore
417/// fn main() {
418///     tokio::runtime::Builder::new_current_thread()
419///         .enable_all()
420///         .start_paused(true)
421///         .build()
422///         .unwrap()
423///         .block_on(async {
424///             println!("Hello world");
425///         })
426/// }
427/// ```
428///
429/// Note that `start_paused` requires the `test-util` feature to be enabled.
430///
431/// ### Rename package
432///
433/// ```ignore
434/// use tokio as tokio1;
435///
436/// #[tokio1::main(crate = "tokio1")]
437/// async fn main() {
438///     println!("Hello world");
439/// }
440/// ```
441///
442/// Equivalent code not using `#[tokio::main]`
443///
444/// ```ignore
445/// use tokio as tokio1;
446///
447/// fn main() {
448///     tokio1::runtime::Builder::new_multi_thread()
449///         .enable_all()
450///         .build()
451///         .unwrap()
452///         .block_on(async {
453///             println!("Hello world");
454///         })
455/// }
456/// ```
457///
458/// ### Configure unhandled panic behavior
459///
460/// Available options are `shutdown_runtime` and `ignore`. For more details, see
461/// [`Builder::unhandled_panic`].
462///
463/// This option is only compatible with the `current_thread` runtime.
464///
465/// ```no_run, ignore
466/// # #![allow(unknown_lints, unexpected_cfgs)]
467/// #[cfg(tokio_unstable)]
468/// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
469/// async fn main() {
470///     let _ = tokio::spawn(async {
471///         panic!("This panic will shutdown the runtime.");
472///     }).await;
473/// }
474/// # #[cfg(not(tokio_unstable))]
475/// # fn main() { }
476/// ```
477///
478/// Equivalent code not using `#[tokio::main]`
479///
480/// ```no_run, ignore
481/// # #![allow(unknown_lints, unexpected_cfgs)]
482/// #[cfg(tokio_unstable)]
483/// fn main() {
484///     tokio::runtime::Builder::new_current_thread()
485///         .enable_all()
486///         .unhandled_panic(UnhandledPanic::ShutdownRuntime)
487///         .build()
488///         .unwrap()
489///         .block_on(async {
490///             let _ = tokio::spawn(async {
491///                 panic!("This panic will shutdown the runtime.");
492///             }).await;
493///         })
494/// }
495/// # #[cfg(not(tokio_unstable))]
496/// # fn main() { }
497/// ```
498///
499/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
500/// documentation on unstable features][unstable] for details on how to enable
501/// Tokio's unstable features.
502///
503/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
504/// [unstable]: ../tokio/index.html#unstable-features
505#[proc_macro_attribute]
506pub fn main(_args: TokenStream, item: TokenStream) -> TokenStream {
507    let input = parse_macro_input!(item as syn::ItemFn);
508
509    let mut fn_body = input.block.clone();
510    let fn_attrs = input.attrs.clone();
511    let fn_vis = input.vis.clone();
512    let _fn_sig = input.sig.clone();
513
514    fn_body
515        .stmts
516        .push(parse_quote!({ Ok::<(), Box<dyn std::error::Error>>(()) }));
517
518    let output = quote! {
519        #(#fn_attrs)*
520        #fn_vis fn main() -> Result<(), Box<dyn std::error::Error>> {
521            ::sword::__internal::tokio_runtime::Builder::new_multi_thread()
522                .enable_all()
523                .build()
524                .expect("Failed building the Runtime")
525                .block_on( async #fn_body )
526        }
527    };
528
529    output.into()
530}