//! Reporter implementations for various standard types. use crate::Reporter; use commonware_actor::Feedback; 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 ignored if the [Reporter] is `None`. impl> Reporter for Option { type Activity = A; fn report(&mut self, activity: Self::Activity) -> Feedback { let Some(reporter) = self else { return Feedback::Ok; }; reporter.report(activity) } } /// 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; fn report(&mut self, activity: Self::Activity) -> Feedback { // This approach avoids cloning activity, if possible. match (&mut self.r1, &mut self.r2) { (Some(r1), Some(r2)) => combine(r1.report(activity.clone()), r2.report(activity)), (Some(r1), None) => r1.report(activity), (None, Some(r2)) => r2.report(activity), (None, None) => Feedback::Ok, } } } const fn combine(a: Feedback, b: Feedback) -> Feedback { match (a, b) { (Feedback::Closed, _) | (_, Feedback::Closed) => Feedback::Closed, (Feedback::Backoff, _) | (_, Feedback::Backoff) => Feedback::Backoff, (Feedback::Ok, Feedback::Ok) => Feedback::Ok, } } 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_actor::Feedback; use commonware_utils::acknowledgement::{Acknowledgement, Exact}; use futures::FutureExt; #[derive(Clone, Debug)] struct SimpleAcknowledger; impl crate::Reporter for SimpleAcknowledger { type Activity = Exact; fn report(&mut self, activity: Self::Activity) -> Feedback { activity.acknowledge(); Feedback::Ok } } #[test] fn optional_branch_acknowledges() { let mut reporters = Reporters::::from(( Some(SimpleAcknowledger), None, )); let (ack, waiter) = Exact::handle(); assert_eq!(reporters.report(ack), Feedback::Ok); assert!( waiter.now_or_never().unwrap().is_ok(), "Waiter did not resolve successfully" ); } #[test] fn absent_reporter_ignores_activity() { let mut reporter = Option::::None; let (ack, waiter) = Exact::handle(); assert_eq!(reporter.report(ack), Feedback::Ok); assert!(waiter.now_or_never().unwrap().is_err()); } #[test] fn combine_returns_worst_feedback() { assert_eq!( combine(Feedback::Closed, Feedback::Backoff), Feedback::Closed ); assert_eq!(combine(Feedback::Backoff, Feedback::Ok), Feedback::Backoff); assert_eq!(combine(Feedback::Ok, Feedback::Ok), Feedback::Ok); } }