//! Reporter implementations for various standard types. use crate::Reporter; use futures::join; use std::marker::PhantomData; /// An implementation of [Reporter] for an optional [Reporter]. /// /// This is useful for reporting activity to a [Reporter] that may not be present. /// Reporting is a no-op if the [Reporter] is `None`. impl> Reporter for Option { type Activity = A; async fn report(&mut self, activity: Self::Activity) { let Some(reporter) = self else { return; }; reporter.report(activity).await; } } /// A struct used to report activity to multiple [Reporter]s (which may or may not be present). #[derive(Clone)] pub struct Reporters { r1: Option, r2: Option, _phantom: PhantomData, } impl Reporter for Reporters where A: Clone + Send + 'static, R1: Reporter, R2: Reporter, { type Activity = A; async fn report(&mut self, activity: Self::Activity) { // This approach avoids cloning activity, if possible. match (&mut self.r1, &mut self.r2) { (Some(r1), Some(r2)) => join!(r1.report(activity.clone()), r2.report(activity)), (Some(r1), None) => (r1.report(activity).await, ()), (None, Some(r2)) => ((), r2.report(activity).await), (None, None) => ((), ()), }; } } impl From<(Option, Option)> for Reporters { fn from((r1, r2): (Option, Option)) -> Self { Self { r1, r2, _phantom: PhantomData, } } } impl From<(Option, R2)> for Reporters { fn from((r1, r2): (Option, R2)) -> Self { Self { r1, r2: Some(r2), _phantom: PhantomData, } } } impl From<(R1, Option)> for Reporters { fn from((r1, r2): (R1, Option)) -> Self { Self { r1: Some(r1), r2, _phantom: PhantomData, } } } impl From<(R1, R2)> for Reporters { fn from((r1, r2): (R1, R2)) -> Self { Self { r1: Some(r1), r2: Some(r2), _phantom: PhantomData, } } } #[cfg(test)] mod tests { use super::*; use commonware_macros::test_async; use commonware_utils::acknowledgement::{Acknowledgement, Exact}; use futures::FutureExt; #[derive(Clone, Debug)] struct SimpleAcknowledger; impl crate::Reporter for SimpleAcknowledger { type Activity = Exact; async fn report(&mut self, activity: Self::Activity) { activity.acknowledge(); } } #[test_async] async fn optional_branch_acknowledges() { let mut reporters = Reporters::::from(( Some(SimpleAcknowledger), None, )); let (ack, waiter) = Exact::handle(); reporters.report(ack).await; assert!( waiter.now_or_never().unwrap().is_ok(), "Waiter did not resolve successfully" ); } }