Skip to main content

commonware_macros/
lib.rs

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