Skip to main content

commonware_macros/
lib.rs

1//! Augment the development of primitives with procedural macros.
2//!
3//! This crate provides:
4//! - [`select!`] - Biased async select over multiple futures (requires `std` feature)
5//! - [`select_loop!`] - Continuous select loop with shutdown handling (requires `std` feature)
6//! - [`stability`], [`stability_mod!`], [`stability_scope!`] - API stability annotations
7//! - [`test_async`], [`test_traced`], [`test_collect_traces`] - Test utilities
8//! - [`test_group`] - Nextest filter group annotations
9
10#![doc(
11    html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
12    html_favicon_url = "https://commonware.xyz/favicon.ico"
13)]
14#![cfg_attr(not(any(feature = "std", test)), no_std)]
15
16/// Select the first future that completes (biased by order).
17///
18/// This macro is powered by [tokio::select!](https://docs.rs/tokio/latest/tokio/macro.select.html)
19/// in biased mode and is not bound to a particular executor or context.
20///
21/// # Example
22///
23/// ```rust
24/// use std::time::Duration;
25/// use futures::executor::block_on;
26/// use futures_timer::Delay;
27///
28/// async fn task() -> usize {
29///     42
30/// }
31///
32/// block_on(async move {
33///     commonware_macros::select! {
34///         _ = Delay::new(Duration::from_secs(1)) => {
35///             println!("timeout fired");
36///         },
37///         v = task() => {
38///             println!("task completed with value: {}", v);
39///         },
40///     };
41/// });
42/// ```
43#[cfg(feature = "std")]
44pub use commonware_macros_impl::select;
45/// Convenience macro to continuously [select!] over a set of futures in biased order,
46/// with a required shutdown handler.
47///
48/// This macro automatically creates a shutdown future from the provided context and requires a
49/// shutdown handler block. The shutdown future is created outside the loop, allowing it to
50/// persist across iterations until shutdown is signaled. The shutdown branch is always checked
51/// first (biased).
52///
53/// After the shutdown block is executed, the loop breaks by default. If different control flow
54/// is desired (such as returning from the enclosing function), it must be handled explicitly.
55///
56/// # Syntax
57///
58/// ```rust,ignore
59/// commonware_macros::select_loop! {
60///     context,
61///     on_start => { /* optional: runs at start of each iteration */ },
62///     on_stopped => { cleanup },
63///     pattern = future => body,
64///     Some(x) = future else break => body,  // refutable pattern with else clause
65///     // ...
66///     on_end => { /* optional: runs after select, skipped on shutdown/break/return/continue */ },
67/// }
68/// ```
69///
70/// The order of blocks matches execution order:
71/// 1. `on_start` (optional) - Runs at the start of each loop iteration, before the select.
72///    Can use `continue` to skip the select or `break` to exit the loop.
73/// 2. `on_stopped` (required) - The shutdown handler, executed when shutdown is signaled.
74/// 3. Select arms - The futures to select over.
75/// 4. `on_end` (optional) - Runs after select completes. Skipped on shutdown or if an arm
76///    uses `break`/`return`/`continue`. Useful for per-iteration post-processing.
77///
78/// All blocks share the same lexical scope within the loop body. Variables declared in
79/// `on_start` are visible in the select arms, `on_stopped`, and `on_end`. This allows
80/// preparing state in `on_start` and using it throughout the iteration.
81///
82/// The `shutdown` variable (the future from `context.stopped()`) is accessible in the
83/// shutdown block, allowing explicit cleanup such as `drop(shutdown)` before breaking or returning.
84///
85/// # Refutable Patterns with `else`
86///
87/// For refutable patterns (patterns that may not match), use the `else` clause to specify
88/// what happens when the pattern fails to match. This uses Rust's let-else syntax internally:
89///
90/// ```rust,ignore
91/// // Option handling
92/// Some(msg) = rx.recv() else break => { handle(msg); }
93/// Some(msg) = rx.recv() else return => { handle(msg); }
94/// Some(msg) = rx.recv() else continue => { handle(msg); }
95///
96/// // Result handling
97/// Ok(value) = result_stream.recv() else break => { process(value); }
98///
99/// // Enum variants
100/// MyEnum::Data(x) = stream.recv() else continue => { use_data(x); }
101/// ```
102///
103/// This replaces the common pattern:
104/// ```rust,ignore
105/// // Before
106/// msg = mailbox.recv() => {
107///     let Some(msg) = msg else { break };
108///     // use msg
109/// }
110///
111/// // After
112/// Some(msg) = mailbox.recv() else break => {
113///     // use msg directly
114/// }
115/// ```
116///
117/// # Expansion
118///
119/// The macro expands to roughly the following code:
120///
121/// ```rust,ignore
122/// // Input:
123/// select_loop! {
124///     context,
125///     on_start => { start_code },
126///     on_stopped => { shutdown_code },
127///     pattern = future => { body },
128///     Some(msg) = rx.recv() else break => { handle(msg) },
129///     on_end => { end_code },
130/// }
131///
132/// // Expands to:
133/// {
134///     let mut shutdown = context.stopped();
135///     loop {
136///         // on_start runs at the beginning of each iteration
137///         { start_code }
138///
139///         select_biased! {
140///             // Shutdown branch (always first due to biased select)
141///             _ = &mut shutdown => {
142///                 { shutdown_code }
143///                 break; // on_end is NOT executed on shutdown
144///             },
145///
146///             // Regular pattern branch
147///             pattern = future => {
148///                 { body }
149///             },
150///
151///             // Refutable pattern with else clause (uses let-else)
152///             __select_result = rx.recv() => {
153///                 let Some(msg) = __select_result else { break };
154///                 { handle(msg) }
155///             },
156///         }
157///
158///         // on_end runs after select completes (skipped on shutdown/break/return/continue)
159///         { end_code }
160///     }
161/// }
162/// ```
163///
164/// # Example
165///
166/// ```rust,ignore
167/// use commonware_macros::select_loop;
168///
169/// async fn run(context: impl commonware_runtime::Spawner, mut receiver: Receiver<Message>) {
170///     let mut counter = 0;
171///     commonware_macros::select_loop! {
172///         context,
173///         on_start => {
174///             // Prepare state for this iteration (visible in arms and on_end)
175///             let start_time = std::time::Instant::now();
176///             counter += 1;
177///         },
178///         on_stopped => {
179///             println!("shutting down after {} iterations", counter);
180///             drop(shutdown);
181///         },
182///         // Refutable pattern: breaks when channel closes (None)
183///         Some(msg) = receiver.recv() else break => {
184///             println!("received: {:?}", msg);
185///         },
186///         on_end => {
187///             // Access variables from on_start
188///             println!("iteration took {:?}", start_time.elapsed());
189///         },
190///     }
191/// }
192/// ```
193#[cfg(feature = "std")]
194pub use commonware_macros_impl::select_loop;
195/// Marks an item with a stability level.
196///
197/// When building with `RUSTFLAGS="--cfg commonware_stability_X"`, items with stability
198/// less than X are excluded. Unmarked items are always included.
199///
200/// See [commonware README](https://github.com/commonwarexyz/monorepo#stability) for stability level definitions.
201///
202/// # Example
203/// ```rust,ignore
204/// use commonware_macros::stability;
205///
206/// #[stability(BETA)]  // excluded at GAMMA, DELTA, EPSILON
207/// pub struct StableApi { }
208/// ```
209///
210/// # Limitation: `#[macro_export]` macros
211///
212/// Due to a Rust limitation ([rust-lang/rust#52234](https://github.com/rust-lang/rust/issues/52234)),
213/// `#[macro_export]` macros cannot be placed inside `stability_scope!` or use `#[stability]`.
214/// Macro-expanded `#[macro_export]` macros cannot be referenced by absolute paths.
215///
216/// For `#[macro_export]` macros, use manual cfg attributes instead:
217/// ```rust,ignore
218/// #[cfg(not(any(
219///     commonware_stability_GAMMA,
220///     commonware_stability_DELTA,
221///     commonware_stability_EPSILON,
222///     commonware_stability_RESERVED
223/// )))] // BETA
224/// #[macro_export]
225/// macro_rules! my_macro { ... }
226/// ```
227pub use commonware_macros_impl::stability;
228/// Marks a module with a stability level.
229///
230/// When building with `RUSTFLAGS="--cfg commonware_stability_N"`, modules with stability
231/// less than N are excluded.
232///
233/// # Example
234/// ```rust,ignore
235/// use commonware_macros::stability_mod;
236///
237/// stability_mod!(BETA, pub mod stable_module);
238/// ```
239pub use commonware_macros_impl::stability_mod;
240/// Marks all items within a scope with a stability level and optional cfg predicate.
241///
242/// When building with `RUSTFLAGS="--cfg commonware_stability_N"`, items with stability
243/// less than N are excluded.
244///
245/// # Example
246/// ```rust,ignore
247/// use commonware_macros::stability_scope;
248///
249/// // Without cfg predicate
250/// stability_scope!(BETA {
251///     pub mod stable_module;
252///     pub use crate::stable_module::Item;
253/// });
254///
255/// // With cfg predicate
256/// stability_scope!(BETA, cfg(feature = "std") {
257///     pub mod std_only_module;
258/// });
259/// ```
260///
261/// # Limitation: `#[macro_export]` macros
262///
263/// `#[macro_export]` macros cannot be placed inside `stability_scope!` due to a Rust
264/// limitation ([rust-lang/rust#52234](https://github.com/rust-lang/rust/issues/52234)).
265/// Use manual cfg attributes instead. See [`stability`] for details.
266pub use commonware_macros_impl::stability_scope;
267/// Run a test function asynchronously.
268///
269/// This macro is powered by the [futures](https://docs.rs/futures) crate
270/// and is not bound to a particular executor or context.
271///
272/// # Example
273///
274/// ```rust
275/// #[commonware_macros::test_async]
276/// async fn test_async_fn() {
277///    assert_eq!(2 + 2, 4);
278/// }
279/// ```
280pub use commonware_macros_impl::test_async;
281/// Capture logs from a test run into an in-memory store.
282///
283/// This macro defaults to a log level of `DEBUG` on the `tracing_subscriber::fmt` layer if no level is provided.
284///
285/// This macro is powered by the [tracing](https://docs.rs/tracing),
286/// [tracing-subscriber](https://docs.rs/tracing-subscriber), and
287/// [commonware-runtime](https://docs.rs/commonware-runtime) crates.
288///
289/// # Note
290///
291/// This macro requires the resolution of the `commonware-runtime`, `tracing`, and `tracing_subscriber` crates.
292///
293/// # Example
294/// ```rust,ignore
295/// use commonware_runtime::telemetry::traces::collector::TraceStorage;
296/// use tracing::{debug, info};
297///
298/// #[commonware_macros::test_collect_traces("INFO")]
299/// fn test_info_level(traces: TraceStorage) {
300///     // Filter applies to console output (FmtLayer)
301///     info!("This is an info log");
302///     debug!("This is a debug log (won't be shown in console output)");
303///
304///     // All traces are collected, regardless of level, by the CollectingLayer.
305///     assert_eq!(traces.get_all().len(), 2);
306/// }
307/// ```
308pub use commonware_macros_impl::test_collect_traces;
309/// Prefix a test name with a nextest filter group.
310///
311/// This renames `test_some_behavior` into `test_some_behavior_<group>_`, making
312/// it easy to filter tests by group postfixes in nextest.
313pub use commonware_macros_impl::test_group;
314/// Capture logs (based on the provided log level) from a test run using
315/// [libtest's output capture functionality](https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output).
316///
317/// This macro defaults to a log level of `DEBUG` if no level is provided.
318///
319/// This macro is powered by the [tracing](https://docs.rs/tracing) and
320/// [tracing-subscriber](https://docs.rs/tracing-subscriber) crates.
321///
322/// # Example
323///
324/// ```rust
325/// use tracing::{debug, info};
326///
327/// #[commonware_macros::test_traced("INFO")]
328/// fn test_info_level() {
329///     info!("This is an info log");
330///     debug!("This is a debug log (won't be shown)");
331///     assert_eq!(2 + 2, 4);
332/// }
333/// ```
334pub use commonware_macros_impl::test_traced;
335
336#[doc(hidden)]
337#[cfg(feature = "std")]
338pub mod __reexport {
339    pub use tokio;
340}