spring_macros/lib.rs
1//! [](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}