eventuary_core/io/
acker.rs1mod batched;
2mod either;
3mod noop;
4mod once;
5
6use std::future::Future;
7use std::sync::Arc;
8
9use futures::future::BoxFuture;
10
11use crate::error::Result;
12
13pub use ::either::Either;
14pub use batched::{AckBuffer, AckBufferConfig, AckCmd, BatchFlusher, BatchedAcker};
15pub use noop::NoopAcker;
16pub use once::OnceAcker;
17
18pub trait Acker: Send + Sync {
26 fn ack(&self) -> impl Future<Output = Result<()>> + Send;
27 fn nack(&self) -> impl Future<Output = Result<()>> + Send;
28}
29
30impl<T: Acker + ?Sized> Acker for Arc<T> {
31 fn ack(&self) -> impl Future<Output = Result<()>> + Send {
32 (**self).ack()
33 }
34 fn nack(&self) -> impl Future<Output = Result<()>> + Send {
35 (**self).nack()
36 }
37}
38
39impl<T: Acker + ?Sized> Acker for Box<T> {
40 fn ack(&self) -> impl Future<Output = Result<()>> + Send {
41 (**self).ack()
42 }
43 fn nack(&self) -> impl Future<Output = Result<()>> + Send {
44 (**self).nack()
45 }
46}
47
48pub trait DynAcker: Send + Sync {
49 fn ack_dyn<'a>(&'a self) -> BoxFuture<'a, Result<()>>;
50 fn nack_dyn<'a>(&'a self) -> BoxFuture<'a, Result<()>>;
51}
52
53impl<T: Acker + ?Sized> DynAcker for T {
54 fn ack_dyn<'a>(&'a self) -> BoxFuture<'a, Result<()>> {
55 Box::pin(<Self as Acker>::ack(self))
56 }
57 fn nack_dyn<'a>(&'a self) -> BoxFuture<'a, Result<()>> {
58 Box::pin(<Self as Acker>::nack(self))
59 }
60}
61
62pub type BoxAcker = Box<dyn DynAcker>;
63pub type ArcAcker = Arc<dyn DynAcker>;
64
65impl Acker for dyn DynAcker + '_ {
66 fn ack(&self) -> impl Future<Output = Result<()>> + Send {
67 DynAcker::ack_dyn(self)
68 }
69 fn nack(&self) -> impl Future<Output = Result<()>> + Send {
70 DynAcker::nack_dyn(self)
71 }
72}
73
74pub trait AckerExt: Acker + Sized + 'static {
75 fn into_boxed(self) -> BoxAcker {
76 Box::new(self)
77 }
78
79 fn into_arced(self) -> ArcAcker {
80 Arc::new(self)
81 }
82}
83
84impl<T: Acker + 'static> AckerExt for T {}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 use std::sync::atomic::{AtomicUsize, Ordering};
91
92 struct CountingAcker {
93 acks: Arc<AtomicUsize>,
94 nacks: Arc<AtomicUsize>,
95 }
96
97 impl Acker for CountingAcker {
98 async fn ack(&self) -> Result<()> {
99 self.acks.fetch_add(1, Ordering::SeqCst);
100 Ok(())
101 }
102 async fn nack(&self) -> Result<()> {
103 self.nacks.fetch_add(1, Ordering::SeqCst);
104 Ok(())
105 }
106 }
107
108 fn counters() -> (Arc<AtomicUsize>, Arc<AtomicUsize>) {
109 (Arc::new(AtomicUsize::new(0)), Arc::new(AtomicUsize::new(0)))
110 }
111
112 #[tokio::test]
113 async fn into_boxed_yields_dyn_acker() {
114 let (acks, nacks) = counters();
115 let acker: BoxAcker = CountingAcker {
116 acks: Arc::clone(&acks),
117 nacks: Arc::clone(&nacks),
118 }
119 .into_boxed();
120 acker.ack().await.unwrap();
121 acker.nack().await.unwrap();
122 assert_eq!(acks.load(Ordering::SeqCst), 1);
123 assert_eq!(nacks.load(Ordering::SeqCst), 1);
124 }
125
126 #[tokio::test]
127 async fn into_arced_yields_shared_acker() {
128 let (acks, _) = counters();
129 let acker: ArcAcker = CountingAcker {
130 acks: Arc::clone(&acks),
131 nacks: Arc::new(AtomicUsize::new(0)),
132 }
133 .into_arced();
134 let clone = Arc::clone(&acker);
135 acker.ack().await.unwrap();
136 clone.ack().await.unwrap();
137 assert_eq!(acks.load(Ordering::SeqCst), 2);
138 }
139
140 #[tokio::test]
141 async fn box_blanket_passes_as_generic_acker() {
142 async fn take<A: Acker>(a: A) {
143 a.ack().await.unwrap();
144 }
145 let (acks, nacks) = counters();
146 let boxed: BoxAcker = CountingAcker {
147 acks: Arc::clone(&acks),
148 nacks,
149 }
150 .into_boxed();
151 take(boxed).await;
152 assert_eq!(acks.load(Ordering::SeqCst), 1);
153 }
154
155 #[tokio::test]
156 async fn arc_blanket_passes_as_generic_acker() {
157 async fn take<A: Acker>(a: A) {
158 a.ack().await.unwrap();
159 }
160 let (acks, nacks) = counters();
161 let arced: ArcAcker = CountingAcker {
162 acks: Arc::clone(&acks),
163 nacks,
164 }
165 .into_arced();
166 take(arced).await;
167 assert_eq!(acks.load(Ordering::SeqCst), 1);
168 }
169}