Skip to main content

commonware_consensus/
reporter.rs

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