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