spring_macros/
lib.rs

1//! [![spring-rs](https://img.shields.io/github/stars/spring-rs/spring-rs)](https://spring-rs.github.io)
2#![doc(html_favicon_url = "https://spring-rs.github.io/favicon.ico")]
3#![doc(html_logo_url = "https://spring-rs.github.io/logo.svg")]
4
5mod auto;
6mod config;
7mod inject;
8mod job;
9mod nest;
10mod route;
11mod stream;
12
13use proc_macro::TokenStream;
14use syn::DeriveInput;
15
16/// Creates resource handler, allowing multiple HTTP method guards.
17///
18/// # Syntax
19/// ```plain
20/// #[route("path", method="HTTP_METHOD"[, attributes])]
21/// ```
22///
23/// # Attributes
24/// - `"path"`: Raw literal string with path for which to register handler.
25/// - `method = "HTTP_METHOD"`: Registers HTTP method to provide guard for. Upper-case string,
26///   "GET", "POST" for example.
27///
28/// # Examples
29/// ```
30/// # use spring_web::axum::response::IntoResponse;
31/// # use spring_macros::route;
32/// #[route("/test", method = "GET", method = "HEAD")]
33/// async fn example() -> impl IntoResponse {
34///     "hello world"
35/// }
36/// ```
37#[proc_macro_attribute]
38pub fn route(args: TokenStream, input: TokenStream) -> TokenStream {
39    route::with_method(None, args, input)
40}
41
42/// Creates resource handler, allowing multiple HTTP methods and paths.
43///
44/// # Syntax
45/// ```plain
46/// #[routes]
47/// #[<method>("path", ...)]
48/// #[<method>("path", ...)]
49/// ...
50/// ```
51///
52/// # Attributes
53/// The `routes` macro itself has no parameters, but allows specifying the attribute macros for
54/// the multiple paths and/or methods, e.g. [`GET`](macro@get) and [`POST`](macro@post).
55///
56/// These helper attributes take the same parameters as the [single method handlers](crate#single-method-handler).
57///
58/// # Examples
59/// ```
60/// # use spring_web::axum::response::IntoResponse;
61/// # use spring_macros::routes;
62/// #[routes]
63/// #[get("/test")]
64/// #[get("/test2")]
65/// #[delete("/test")]
66/// async fn example() -> impl IntoResponse {
67///     "hello world"
68/// }
69/// ```
70#[proc_macro_attribute]
71pub fn routes(_: TokenStream, input: TokenStream) -> TokenStream {
72    route::with_methods(input)
73}
74
75macro_rules! method_macro {
76    ($variant:ident, $method:ident) => {
77        ///
78        /// # Syntax
79        /// ```plain
80        #[doc = concat!("#[", stringify!($method), r#"("path"[, attributes])]"#)]
81        /// ```
82        ///
83        /// # Attributes
84        /// - `"path"`: Raw literal string with path for which to register handler.
85        ///
86        /// # Examples
87        /// ```
88        /// # use spring_web::axum::response::IntoResponse;
89        #[doc = concat!("# use spring_macros::", stringify!($method), ";")]
90        #[doc = concat!("#[", stringify!($method), r#"("/")]"#)]
91        /// async fn example() -> impl IntoResponse {
92        ///     "hello world"
93        /// }
94        /// ```
95        #[proc_macro_attribute]
96        pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
97            route::with_method(Some(route::Method::$variant), args, input)
98        }
99    };
100}
101
102method_macro!(Get, get);
103method_macro!(Post, post);
104method_macro!(Put, put);
105method_macro!(Delete, delete);
106method_macro!(Head, head);
107method_macro!(Options, options);
108method_macro!(Trace, trace);
109method_macro!(Patch, patch);
110
111/// Prepends a path prefix to all handlers using routing macros inside the attached module.
112///
113/// # Syntax
114///
115/// ```
116/// # use spring_macros::nest;
117/// #[nest("/prefix")]
118/// mod api {
119///     // ...
120/// }
121/// ```
122///
123/// # Arguments
124///
125/// - `"/prefix"` - Raw literal string to be prefixed onto contained handlers' paths.
126///
127/// # Example
128///
129/// ```
130/// # use spring_macros::{nest, get};
131/// # use spring_web::axum::response::IntoResponse;
132/// #[nest("/api")]
133/// mod api {
134///     # use super::*;
135///     #[get("/hello")]
136///     pub async fn hello() -> impl IntoResponse {
137///         // this has path /api/hello
138///         "Hello, world!"
139///     }
140/// }
141/// # fn main() {}
142/// ```
143#[proc_macro_attribute]
144pub fn nest(args: TokenStream, input: TokenStream) -> TokenStream {
145    nest::with_nest(args, input)
146}
147
148fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
149    let compile_err = TokenStream::from(err.to_compile_error());
150    item.extend(compile_err);
151    item
152}
153
154/// Job
155///
156macro_rules! job_macro {
157    ($variant:ident, $job_type:ident, $example:literal) => {
158        ///
159        /// # Syntax
160        /// ```plain
161        #[doc = concat!("#[", stringify!($job_type), "(", $example, ")]")]
162        /// ```
163        ///
164        /// # Attributes
165        /// - `"path"`: Raw literal string with path for which to register handler.
166        ///
167        /// # Examples
168        /// ```
169        /// # use spring_web::axum::response::IntoResponse;
170        #[doc = concat!("# use spring_macros::", stringify!($job_type), ";")]
171        #[doc = concat!("#[", stringify!($job_type), "(", stringify!($example), ")]")]
172        /// async fn example() {
173        ///     println!("hello world");
174        /// }
175        /// ```
176        #[proc_macro_attribute]
177        pub fn $job_type(args: TokenStream, input: TokenStream) -> TokenStream {
178            job::with_job(job::JobType::$variant, args, input)
179        }
180    };
181}
182
183job_macro!(OneShot, one_shot, 60);
184job_macro!(FixDelay, fix_delay, 60);
185job_macro!(FixRate, fix_rate, 60);
186job_macro!(Cron, cron, "1/10 * * * * *");
187
188/// Auto config
189/// ```diff
190///  use spring_macros::auto_config;
191///  use spring_web::{WebPlugin, WebConfigurator};
192///  use spring_job::{JobPlugin, JobConfigurator};
193///  use spring_boot::app::App;
194/// +#[auto_config(WebConfigurator, JobConfigurator)]
195///  #[tokio::main]
196///  async fn main() {
197///      App::new()
198///         .add_plugin(WebPlugin)
199///         .add_plugin(JobPlugin)
200/// -       .add_router(router())
201/// -       .add_jobs(jobs())
202///         .run()
203///         .await
204///  }
205/// ```
206///
207#[proc_macro_attribute]
208pub fn auto_config(args: TokenStream, input: TokenStream) -> TokenStream {
209    auto::config(args, input)
210}
211
212/// stream macro
213#[proc_macro_attribute]
214pub fn stream_listener(args: TokenStream, input: TokenStream) -> TokenStream {
215    stream::listener(args, input)
216}
217
218/// Configurable
219#[proc_macro_derive(Configurable, attributes(config_prefix))]
220pub fn derive_config(input: TokenStream) -> TokenStream {
221    let input = syn::parse_macro_input!(input as DeriveInput);
222
223    config::expand_derive(input)
224        .unwrap_or_else(syn::Error::into_compile_error)
225        .into()
226}
227
228/// Injectable Servcie
229#[proc_macro_derive(Service, attributes(prototype, inject))]
230pub fn derive_service(input: TokenStream) -> TokenStream {
231    let input = syn::parse_macro_input!(input as DeriveInput);
232
233    inject::expand_derive(input)
234        .unwrap_or_else(syn::Error::into_compile_error)
235        .into()
236}