1use crate::Reporter;
4use commonware_actor::Feedback;
5use std::marker::PhantomData;
6
7impl<A: Send, R: Reporter<Activity = A>> Reporter for Option<R> {
12 type Activity = A;
13
14 fn report(&mut self, activity: Self::Activity) -> Feedback {
15 let Some(reporter) = self else {
16 return Feedback::Ok;
17 };
18 reporter.report(activity)
19 }
20}
21
22#[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 fn report(&mut self, activity: Self::Activity) -> Feedback {
40 match (&mut self.r1, &mut self.r2) {
42 (Some(r1), Some(r2)) => combine(r1.report(activity.clone()), r2.report(activity)),
43 (Some(r1), None) => r1.report(activity),
44 (None, Some(r2)) => r2.report(activity),
45 (None, None) => Feedback::Ok,
46 }
47 }
48}
49
50const fn combine(a: Feedback, b: Feedback) -> Feedback {
51 match (a, b) {
52 (Feedback::Closed, _) | (_, Feedback::Closed) => Feedback::Closed,
53 (Feedback::Backoff, _) | (_, Feedback::Backoff) => Feedback::Backoff,
54 (Feedback::Ok, Feedback::Ok) => Feedback::Ok,
55 }
56}
57
58impl<A, R1, R2> From<(Option<R1>, Option<R2>)> for Reporters<A, R1, R2> {
59 fn from((r1, r2): (Option<R1>, Option<R2>)) -> Self {
60 Self {
61 r1,
62 r2,
63 _phantom: PhantomData,
64 }
65 }
66}
67
68impl<A, R1, R2> From<(Option<R1>, R2)> for Reporters<A, R1, R2> {
69 fn from((r1, r2): (Option<R1>, R2)) -> Self {
70 Self {
71 r1,
72 r2: Some(r2),
73 _phantom: PhantomData,
74 }
75 }
76}
77
78impl<A, R1, R2> From<(R1, Option<R2>)> for Reporters<A, R1, R2> {
79 fn from((r1, r2): (R1, Option<R2>)) -> Self {
80 Self {
81 r1: Some(r1),
82 r2,
83 _phantom: PhantomData,
84 }
85 }
86}
87
88impl<A, R1, R2> From<(R1, R2)> for Reporters<A, R1, R2> {
89 fn from((r1, r2): (R1, R2)) -> Self {
90 Self {
91 r1: Some(r1),
92 r2: Some(r2),
93 _phantom: PhantomData,
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use commonware_actor::Feedback;
102 use commonware_utils::acknowledgement::{Acknowledgement, Exact};
103 use futures::FutureExt;
104
105 #[derive(Clone, Debug)]
106 struct SimpleAcknowledger;
107
108 impl crate::Reporter for SimpleAcknowledger {
109 type Activity = Exact;
110
111 fn report(&mut self, activity: Self::Activity) -> Feedback {
112 activity.acknowledge();
113 Feedback::Ok
114 }
115 }
116
117 #[test]
118 fn optional_branch_acknowledges() {
119 let mut reporters = Reporters::<Exact, SimpleAcknowledger, SimpleAcknowledger>::from((
120 Some(SimpleAcknowledger),
121 None,
122 ));
123
124 let (ack, waiter) = Exact::handle();
125 assert_eq!(reporters.report(ack), Feedback::Ok);
126
127 assert!(
128 waiter.now_or_never().unwrap().is_ok(),
129 "Waiter did not resolve successfully"
130 );
131 }
132
133 #[test]
134 fn absent_reporter_ignores_activity() {
135 let mut reporter = Option::<SimpleAcknowledger>::None;
136 let (ack, waiter) = Exact::handle();
137 assert_eq!(reporter.report(ack), Feedback::Ok);
138 assert!(waiter.now_or_never().unwrap().is_err());
139 }
140
141 #[test]
142 fn combine_returns_worst_feedback() {
143 assert_eq!(
144 combine(Feedback::Closed, Feedback::Backoff),
145 Feedback::Closed
146 );
147 assert_eq!(combine(Feedback::Backoff, Feedback::Ok), Feedback::Backoff);
148 assert_eq!(combine(Feedback::Ok, Feedback::Ok), Feedback::Ok);
149 }
150}