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 let output = if cfg!(feature = "hot-reload") {
515 quote! {
516 async fn __internal_main() -> Result<(), Box<dyn std::error::Error>> {
517 #fn_body
518
519 Ok::<(), Box<dyn std::error::Error>>(())
520 }
521
522 #(#fn_attrs)*
523 #fn_vis fn main() {
524 ::sword::__internal::tokio_runtime::Builder::new_multi_thread()
525 .enable_all()
526 .build()
527 .expect("Failed building the Runtime")
528 .block_on(::sword::__internal::hot_reload::dioxus_devtools::serve_subsecond(__internal_main));
529 }
530 }
531 } else {
532 fn_body
533 .stmts
534 .push(parse_quote!({ Ok::<(), Box<dyn std::error::Error>>(()) }));
535
536 quote! {
537 #(#fn_attrs)*
538 #fn_vis fn main() -> Result<(), Box<dyn std::error::Error>> {
539 ::sword::__internal::tokio_runtime::Builder::new_multi_thread()
540 .enable_all()
541 .build()
542 .expect("Failed building the Runtime")
543 .block_on( async #fn_body )
544 }
545 }
546 };
547
548 output.into()
549}