selectme_macros/lib.rs
1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/selectme-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/selectme)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/selectme-macros.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/selectme-macros)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-selectme--macros-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/selectme-macros)
4//!
5//! Macros for [selectme].
6//!
7//! [selectme]: https://docs.rs/selectme
8
9#![deny(missing_docs)]
10#![deny(rust_2018_idioms)]
11#![deny(rustdoc::broken_intra_doc_links)]
12#![deny(unreachable_pub)]
13#![allow(clippy::needless_doctest_main)]
14
15use proc_macro::TokenStream;
16
17mod entry;
18mod error;
19mod into_tokens;
20mod parsing;
21mod select;
22mod tok;
23mod token_stream;
24
25#[allow(missing_docs)]
26#[proc_macro]
27pub fn select(input: TokenStream) -> TokenStream {
28 select::build(input, select::Mode::Default)
29}
30
31#[allow(missing_docs)]
32#[proc_macro]
33pub fn inline(input: TokenStream) -> TokenStream {
34 select::build(input, select::Mode::Inline)
35}
36
37/// Marks async function to be executed by the selected runtime. This macro
38/// helps set up a `Runtime` without requiring the user to use [Runtime] or
39/// [Builder] directly.
40///
41/// Note: This macro is designed to be simplistic and targets applications that
42/// do not require a complex setup. If the provided functionality is not
43/// sufficient, you may be interested in using [Builder], which provides a more
44/// powerful interface.
45///
46/// Note: This macro can be used on any function and not just the `main`
47/// function. Using it on a non-main function makes the function behave as if it
48/// was synchronous by starting a new runtime each time it is called. If the
49/// function is called often, it is preferable to create the runtime using the
50/// runtime builder so the runtime can be reused across calls.
51///
52/// # Multi-threaded runtime
53///
54/// To use the multi-threaded runtime, the macro can be configured using
55///
56/// ```
57/// #[selectme::main(flavor = "multi_thread", worker_threads = 10)]
58/// # async fn main() {}
59/// ```
60///
61/// The `worker_threads` option configures the number of worker threads, and
62/// defaults to the number of cpus on the system. This is the default flavor.
63///
64/// Note: The multi-threaded runtime requires the `rt-multi-thread` feature
65/// flag.
66///
67/// # Current thread runtime
68///
69/// To use the single-threaded runtime known as the `current_thread` runtime,
70/// the macro can be configured using
71///
72/// ```
73/// #[selectme::main(flavor = "current_thread")]
74/// # async fn main() {}
75/// ```
76///
77/// ## Function arguments:
78///
79/// Arguments are allowed for any functions aside from `main` which is special
80///
81/// ## Usage
82///
83/// ### Using the multi-thread runtime
84///
85/// ```rust
86/// #[selectme::main]
87/// async fn main() {
88/// println!("Hello world");
89/// }
90/// ```
91///
92/// Equivalent code not using `#[selectme::main]`
93///
94/// ```rust
95/// fn main() {
96/// async fn main() {
97/// println!("Hello world");
98/// }
99///
100/// tokio::runtime::Builder::new_multi_thread()
101/// .enable_all()
102/// .build()
103/// .unwrap()
104/// .block_on(main())
105/// }
106/// ```
107///
108/// ### Using current thread runtime
109///
110/// The basic scheduler is single-threaded.
111///
112/// ```rust
113/// #[selectme::main(flavor = "current_thread")]
114/// async fn main() {
115/// println!("Hello world");
116/// }
117/// ```
118///
119/// Equivalent code not using `#[selectme::main]`
120///
121/// ```rust
122/// fn main() {
123/// tokio::runtime::Builder::new_current_thread()
124/// .enable_all()
125/// .build()
126/// .unwrap()
127/// .block_on(async {
128/// println!("Hello world");
129/// })
130/// }
131/// ```
132///
133/// ### Set number of worker threads
134///
135/// ```rust
136/// #[selectme::main(worker_threads = 2)]
137/// async fn main() {
138/// println!("Hello world");
139/// }
140/// ```
141///
142/// Equivalent code not using `#[selectme::main]`
143///
144/// ```rust
145/// fn main() {
146/// async fn main() {
147/// println!("Hello world");
148/// }
149///
150/// tokio::runtime::Builder::new_multi_thread()
151/// .worker_threads(2)
152/// .enable_all()
153/// .build()
154/// .unwrap()
155/// .block_on(main())
156/// }
157/// ```
158///
159/// ### Configure the runtime to start with time paused
160///
161/// ```rust
162/// #[selectme::main(flavor = "current_thread", start_paused = true)]
163/// async fn main() {
164/// println!("Hello world");
165/// }
166/// ```
167///
168/// Equivalent code not using `#[selectme::main]`
169///
170/// ```rust
171/// fn main() {
172/// tokio::runtime::Builder::new_current_thread()
173/// .enable_all()
174/// .start_paused(true)
175/// .build()
176/// .unwrap()
177/// .block_on(async {
178/// println!("Hello world");
179/// })
180/// }
181/// ```
182///
183/// Note that `start_paused` requires the `test-util` feature to be enabled.
184///
185/// ### NOTE:
186///
187/// If you rename the Tokio crate in your dependencies this macro will not work.
188/// If you must rename the current version of Tokio because you're also using an
189/// older version of Tokio, you _must_ make the current version of Tokio
190/// available as `tokio` in the module where this macro is expanded.
191///
192/// [Runtime]: https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html
193/// [Builder]: https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html
194#[proc_macro_attribute]
195pub fn main(args: TokenStream, item_stream: TokenStream) -> TokenStream {
196 crate::entry::build(
197 crate::entry::EntryKind::Main,
198 crate::entry::SupportsThreading::Supported,
199 args,
200 item_stream,
201 )
202}
203
204/// Marks async function to be executed by runtime, suitable to test environment
205///
206/// ## Usage
207///
208/// ### Multi-thread runtime
209///
210/// ```no_run
211/// #[selectme::test(flavor = "multi_thread", worker_threads = 1)]
212/// async fn my_test() {
213/// assert!(true);
214/// }
215/// ```
216///
217/// ### Using default
218///
219/// The default test runtime is single-threaded.
220///
221/// ```no_run
222/// #[selectme::test]
223/// async fn my_test() {
224/// assert!(true);
225/// }
226/// ```
227///
228/// ### Configure the runtime to start with time paused
229///
230/// ```no_run
231/// #[selectme::test(start_paused = true)]
232/// async fn my_test() {
233/// assert!(true);
234/// }
235/// ```
236///
237/// Note that `start_paused` requires the `test-util` feature to be enabled.
238///
239/// ### NOTE:
240///
241/// If you rename the Tokio crate in your dependencies this macro will not work.
242/// If you must rename the current version of Tokio because you're also using an
243/// older version of Tokio, you _must_ make the current version of Tokio
244/// available as `tokio` in the module where this macro is expanded.
245#[proc_macro_attribute]
246pub fn test(args: TokenStream, item_stream: TokenStream) -> TokenStream {
247 crate::entry::build(
248 crate::entry::EntryKind::Test,
249 crate::entry::SupportsThreading::Supported,
250 args,
251 item_stream,
252 )
253}
254
255#[cfg(feature = "tokio-entry")]
256#[allow(missing_docs)]
257#[proc_macro_attribute]
258pub fn main_rt(args: TokenStream, item_stream: TokenStream) -> TokenStream {
259 crate::entry::build(
260 crate::entry::EntryKind::Main,
261 crate::entry::SupportsThreading::NotSupported,
262 args,
263 item_stream,
264 )
265}
266
267#[cfg(feature = "tokio-entry")]
268#[allow(missing_docs)]
269#[proc_macro_attribute]
270pub fn test_rt(args: TokenStream, item_stream: TokenStream) -> TokenStream {
271 crate::entry::build(
272 crate::entry::EntryKind::Test,
273 crate::entry::SupportsThreading::NotSupported,
274 args,
275 item_stream,
276 )
277}
278
279/// Always fails with the error message below.
280/// ```text
281/// The #[selectme::main] macro requires rt or rt-multi-thread.
282/// ```
283#[cfg(feature = "tokio-entry")]
284#[proc_macro_attribute]
285pub fn main_fail(_args: TokenStream, _item: TokenStream) -> TokenStream {
286 error::expand("the #[selectme::main] macro requires rt or rt-multi-thread")
287}
288
289/// Always fails with the error message below.
290/// ```text
291/// The #[selectme::test] macro requires rt or rt-multi-thread.
292/// ```
293#[cfg(feature = "tokio-entry")]
294#[proc_macro_attribute]
295pub fn test_fail(_args: TokenStream, _item: TokenStream) -> TokenStream {
296 error::expand("the #[selectme::test] macro requires rt or rt-multi-thread")
297}