commonware_consensus/
reporter.rs

1//! Reporter implementations for various standard types.
2
3use crate::Reporter;
4use futures::join;
5use std::marker::PhantomData;
6
7/// An implementation of [Reporter] for an optional [Reporter].
8///
9/// This is useful for reporting activity to a [Reporter] that may not be present.
10/// Reporting is a no-op if the [Reporter] is `None`.
11impl<A: Send, R: Reporter<Activity = A>> Reporter for Option<R> {
12    type Activity = A;
13
14    async fn report(&mut self, activity: Self::Activity) {
15        let Some(reporter) = self else {
16            return;
17        };
18        reporter.report(activity).await;
19    }
20}
21
22/// A struct used to report activity to multiple [Reporter]s (which may or may not be present).
23#[derive(Clone)]
24pub struct Reporters<A, R1, R2> {
25    r1: Option<R1>,
26    r2: Option<R2>,
27
28    _phantom: PhantomData<A>,
29}
30
31impl<A, R1, R2> Reporter for Reporters<A, R1, R2>
32where
33    A: Clone + Send + 'static,
34    R1: Reporter<Activity = A>,
35    R2: Reporter<Activity = A>,
36{
37    type Activity = A;
38
39    async fn report(&mut self, activity: Self::Activity) {
40        // This approach avoids cloning activity, if possible.
41        match (&mut self.r1, &mut self.r2) {
42            (Some(r1), Some(r2)) => join!(r1.report(activity.clone()), r2.report(activity)),
43            (Some(r1), None) => (r1.report(activity).await, ()),
44            (None, Some(r2)) => ((), r2.report(activity).await),
45            (None, None) => ((), ()),
46        };
47    }
48}
49
50impl<A, R1, R2> From<(Option<R1>, Option<R2>)> for Reporters<A, R1, R2> {
51    fn from((r1, r2): (Option<R1>, Option<R2>)) -> Self {
52        Self {
53            r1,
54            r2,
55            _phantom: PhantomData,
56        }
57    }
58}
59
60impl<A, R1, R2> From<(Option<R1>, R2)> for Reporters<A, R1, R2> {
61    fn from((r1, r2): (Option<R1>, R2)) -> Self {
62        Self {
63            r1,
64            r2: Some(r2),
65            _phantom: PhantomData,
66        }
67    }
68}
69
70impl<A, R1, R2> From<(R1, Option<R2>)> for Reporters<A, R1, R2> {
71    fn from((r1, r2): (R1, Option<R2>)) -> Self {
72        Self {
73            r1: Some(r1),
74            r2,
75            _phantom: PhantomData,
76        }
77    }
78}
79
80impl<A, R1, R2> From<(R1, R2)> for Reporters<A, R1, R2> {
81    fn from((r1, r2): (R1, R2)) -> Self {
82        Self {
83            r1: Some(r1),
84            r2: Some(r2),
85            _phantom: PhantomData,
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use commonware_macros::test_async;
94    use commonware_utils::acknowledgement::{Acknowledgement, Exact};
95    use futures::FutureExt;
96
97    #[derive(Clone, Debug)]
98    struct SimpleAcknowledger;
99
100    impl crate::Reporter for SimpleAcknowledger {
101        type Activity = Exact;
102
103        async fn report(&mut self, activity: Self::Activity) {
104            activity.acknowledge();
105        }
106    }
107
108    #[test_async]
109    async fn optional_branch_acknowledges() {
110        let mut reporters = Reporters::<Exact, SimpleAcknowledger, SimpleAcknowledger>::from((
111            Some(SimpleAcknowledger),
112            None,
113        ));
114
115        let (ack, waiter) = Exact::handle();
116        reporters.report(ack).await;
117
118        assert!(
119            waiter.now_or_never().unwrap().is_ok(),
120            "Waiter did not resolve successfully"
121        );
122    }
123}