//! Augment the development of primitives with procedural macros. //! //! This crate provides: //! - [`select!`] - Biased async select over multiple futures (requires `std` feature) //! - [`select_loop!`] - Continuous select loop with shutdown handling (requires `std` feature) //! - [`stability`], [`stability_mod!`], [`stability_scope!`] - API stability annotations //! - [`test_async`], [`test_traced`], [`test_collect_traces`] - Test utilities //! - [`test_group`] - Nextest filter group annotations #![doc( html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg", html_favicon_url = "https://commonware.xyz/favicon.ico" )] #![cfg_attr(not(any(feature = "std", test)), no_std)] /// Select the first future that completes (biased by order). /// /// This macro is powered by [tokio::select!](https://docs.rs/tokio/latest/tokio/macro.select.html) /// in biased mode and is not bound to a particular executor or context. /// /// # Example /// /// ```rust /// use std::time::Duration; /// use futures::executor::block_on; /// use futures_timer::Delay; /// /// async fn task() -> usize { /// 42 /// } /// /// block_on(async move { /// commonware_macros::select! { /// _ = Delay::new(Duration::from_secs(1)) => { /// println!("timeout fired"); /// }, /// v = task() => { /// println!("task completed with value: {}", v); /// }, /// }; /// }); /// ``` #[cfg(feature = "std")] pub use commonware_macros_impl::select; /// Convenience macro to continuously [select!] over a set of futures in biased order, /// with a required shutdown handler. /// /// This macro automatically creates a shutdown future from the provided context and requires a /// shutdown handler block. The shutdown future is created outside the loop, allowing it to /// persist across iterations until shutdown is signaled. The shutdown branch is always checked /// first (biased). /// /// After the shutdown block is executed, the loop breaks by default. If different control flow /// is desired (such as returning from the enclosing function), it must be handled explicitly. /// /// # Syntax /// /// ```rust,ignore /// commonware_macros::select_loop! { /// context, /// on_start => { /* optional: runs at start of each iteration */ }, /// on_stopped => { cleanup }, /// pattern = future => body, /// Some(x) = future else break => body, // refutable pattern with else clause /// // ... /// on_end => { /* optional: runs after select, skipped on shutdown/break/return/continue */ }, /// } /// ``` /// /// The order of blocks matches execution order: /// 1. `on_start` (optional) - Runs at the start of each loop iteration, before the select. /// Can use `continue` to skip the select or `break` to exit the loop. /// 2. `on_stopped` (required) - The shutdown handler, executed when shutdown is signaled. /// 3. Select arms - The futures to select over. /// 4. `on_end` (optional) - Runs after select completes. Skipped on shutdown or if an arm /// uses `break`/`return`/`continue`. Useful for per-iteration post-processing. /// /// All blocks share the same lexical scope within the loop body. Variables declared in /// `on_start` are visible in the select arms, `on_stopped`, and `on_end`. This allows /// preparing state in `on_start` and using it throughout the iteration. /// /// The `shutdown` variable (the future from `context.stopped()`) is accessible in the /// shutdown block, allowing explicit cleanup such as `drop(shutdown)` before breaking or returning. /// /// # Refutable Patterns with `else` /// /// For refutable patterns (patterns that may not match), use the `else` clause to specify /// what happens when the pattern fails to match. This uses Rust's let-else syntax internally: /// /// ```rust,ignore /// // Option handling /// Some(msg) = rx.recv() else break => { handle(msg); } /// Some(msg) = rx.recv() else return => { handle(msg); } /// Some(msg) = rx.recv() else continue => { handle(msg); } /// /// // Result handling /// Ok(value) = result_stream.recv() else break => { process(value); } /// /// // Enum variants /// MyEnum::Data(x) = stream.recv() else continue => { use_data(x); } /// ``` /// /// This replaces the common pattern: /// ```rust,ignore /// // Before /// msg = mailbox.recv() => { /// let Some(msg) = msg else { break }; /// // use msg /// } /// /// // After /// Some(msg) = mailbox.recv() else break => { /// // use msg directly /// } /// ``` /// /// # Expansion /// /// The macro expands to roughly the following code: /// /// ```rust,ignore /// // Input: /// select_loop! { /// context, /// on_start => { start_code }, /// on_stopped => { shutdown_code }, /// pattern = future => { body }, /// Some(msg) = rx.recv() else break => { handle(msg) }, /// on_end => { end_code }, /// } /// /// // Expands to: /// { /// let mut shutdown = context.stopped(); /// loop { /// // on_start runs at the beginning of each iteration /// { start_code } /// /// select_biased! { /// // Shutdown branch (always first due to biased select) /// _ = &mut shutdown => { /// { shutdown_code } /// break; // on_end is NOT executed on shutdown /// }, /// /// // Regular pattern branch /// pattern = future => { /// { body } /// }, /// /// // Refutable pattern with else clause (uses let-else) /// __select_result = rx.recv() => { /// let Some(msg) = __select_result else { break }; /// { handle(msg) } /// }, /// } /// /// // on_end runs after select completes (skipped on shutdown/break/return/continue) /// { end_code } /// } /// } /// ``` /// /// # Example /// /// ```rust,ignore /// use commonware_macros::select_loop; /// /// async fn run(context: impl commonware_runtime::Spawner, mut receiver: Receiver) { /// let mut counter = 0; /// commonware_macros::select_loop! { /// context, /// on_start => { /// // Prepare state for this iteration (visible in arms and on_end) /// let start_time = std::time::Instant::now(); /// counter += 1; /// }, /// on_stopped => { /// println!("shutting down after {} iterations", counter); /// drop(shutdown); /// }, /// // Refutable pattern: breaks when channel closes (None) /// Some(msg) = receiver.recv() else break => { /// println!("received: {:?}", msg); /// }, /// on_end => { /// // Access variables from on_start /// println!("iteration took {:?}", start_time.elapsed()); /// }, /// } /// } /// ``` #[cfg(feature = "std")] pub use commonware_macros_impl::select_loop; /// Marks an item with a stability level. /// /// When building with `RUSTFLAGS="--cfg commonware_stability_X"`, items with stability /// less than X are excluded. Unmarked items are always included. /// /// See [commonware README](https://github.com/commonwarexyz/monorepo#stability) for stability level definitions. /// /// # Example /// ```rust,ignore /// use commonware_macros::stability; /// /// #[stability(BETA)] // excluded at GAMMA, DELTA, EPSILON /// pub struct StableApi { } /// ``` /// /// # Limitation: `#[macro_export]` macros /// /// Due to a Rust limitation ([rust-lang/rust#52234](https://github.com/rust-lang/rust/issues/52234)), /// `#[macro_export]` macros cannot be placed inside `stability_scope!` or use `#[stability]`. /// Macro-expanded `#[macro_export]` macros cannot be referenced by absolute paths. /// /// For `#[macro_export]` macros, use manual cfg attributes instead: /// ```rust,ignore /// #[cfg(not(any( /// commonware_stability_GAMMA, /// commonware_stability_DELTA, /// commonware_stability_EPSILON, /// commonware_stability_RESERVED /// )))] // BETA /// #[macro_export] /// macro_rules! my_macro { ... } /// ``` pub use commonware_macros_impl::stability; /// Marks a module with a stability level. /// /// When building with `RUSTFLAGS="--cfg commonware_stability_N"`, modules with stability /// less than N are excluded. /// /// # Example /// ```rust,ignore /// use commonware_macros::stability_mod; /// /// stability_mod!(BETA, pub mod stable_module); /// ``` pub use commonware_macros_impl::stability_mod; /// Marks all items within a scope with a stability level and optional cfg predicate. /// /// When building with `RUSTFLAGS="--cfg commonware_stability_N"`, items with stability /// less than N are excluded. /// /// # Example /// ```rust,ignore /// use commonware_macros::stability_scope; /// /// // Without cfg predicate /// stability_scope!(BETA { /// pub mod stable_module; /// pub use crate::stable_module::Item; /// }); /// /// // With cfg predicate /// stability_scope!(BETA, cfg(feature = "std") { /// pub mod std_only_module; /// }); /// ``` /// /// # Limitation: `#[macro_export]` macros /// /// `#[macro_export]` macros cannot be placed inside `stability_scope!` due to a Rust /// limitation ([rust-lang/rust#52234](https://github.com/rust-lang/rust/issues/52234)). /// Use manual cfg attributes instead. See [`stability`] for details. pub use commonware_macros_impl::stability_scope; /// Run a test function asynchronously. /// /// This macro is powered by the [futures](https://docs.rs/futures) crate /// and is not bound to a particular executor or context. /// /// # Example /// /// ```rust /// #[commonware_macros::test_async] /// async fn test_async_fn() { /// assert_eq!(2 + 2, 4); /// } /// ``` pub use commonware_macros_impl::test_async; /// Capture logs from a test run into an in-memory store. /// /// This macro defaults to a log level of `DEBUG` on the `tracing_subscriber::fmt` layer if no level is provided. /// /// This macro is powered by the [tracing](https://docs.rs/tracing), /// [tracing-subscriber](https://docs.rs/tracing-subscriber), and /// [commonware-runtime](https://docs.rs/commonware-runtime) crates. /// /// # Note /// /// This macro requires the resolution of the `commonware-runtime`, `tracing`, and `tracing_subscriber` crates. /// /// # Example /// ```rust,ignore /// use commonware_runtime::telemetry::traces::collector::TraceStorage; /// use tracing::{debug, info}; /// /// #[commonware_macros::test_collect_traces("INFO")] /// fn test_info_level(traces: TraceStorage) { /// // Filter applies to console output (FmtLayer) /// info!("This is an info log"); /// debug!("This is a debug log (won't be shown in console output)"); /// /// // All traces are collected, regardless of level, by the CollectingLayer. /// assert_eq!(traces.get_all().len(), 2); /// } /// ``` pub use commonware_macros_impl::test_collect_traces; /// Prefix a test name with a nextest filter group. /// /// This renames `test_some_behavior` into `test_some_behavior__`, making /// it easy to filter tests by group postfixes in nextest. pub use commonware_macros_impl::test_group; /// Capture logs (based on the provided log level) from a test run using /// [libtest's output capture functionality](https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output). /// /// This macro defaults to a log level of `DEBUG` if no level is provided. /// /// This macro is powered by the [tracing](https://docs.rs/tracing) and /// [tracing-subscriber](https://docs.rs/tracing-subscriber) crates. /// /// # Example /// /// ```rust /// use tracing::{debug, info}; /// /// #[commonware_macros::test_traced("INFO")] /// fn test_info_level() { /// info!("This is an info log"); /// debug!("This is a debug log (won't be shown)"); /// assert_eq!(2 + 2, 4); /// } /// ``` pub use commonware_macros_impl::test_traced; #[doc(hidden)] #[cfg(feature = "std")] pub mod __reexport { pub use tokio; }