eventuary_core/io/
writer.rs1use std::future::Future;
2use std::sync::Arc;
3
4use futures::future::BoxFuture;
5
6use crate::error::Result;
7use crate::event::Event;
8use crate::payload::Payload;
9
10pub trait Writer<P = Payload>: Send + Sync
11where
12 P: Send + Sync,
13{
14 fn write<'a>(&'a self, event: &'a Event<P>) -> impl Future<Output = Result<()>> + Send + 'a;
15
16 fn write_all<'a>(
17 &'a self,
18 events: &'a [Event<P>],
19 ) -> impl Future<Output = Result<()>> + Send + 'a {
20 async move {
21 for event in events {
22 self.write(event).await?;
23 }
24 Ok(())
25 }
26 }
27}
28
29impl<T: Writer<P> + ?Sized, P: Send + Sync> Writer<P> for Arc<T> {
30 fn write<'a>(&'a self, event: &'a Event<P>) -> impl Future<Output = Result<()>> + Send + 'a {
31 (**self).write(event)
32 }
33
34 fn write_all<'a>(
35 &'a self,
36 events: &'a [Event<P>],
37 ) -> impl Future<Output = Result<()>> + Send + 'a {
38 (**self).write_all(events)
39 }
40}
41
42impl<T: Writer<P> + ?Sized, P: Send + Sync> Writer<P> for Box<T> {
43 fn write<'a>(&'a self, event: &'a Event<P>) -> impl Future<Output = Result<()>> + Send + 'a {
44 (**self).write(event)
45 }
46
47 fn write_all<'a>(
48 &'a self,
49 events: &'a [Event<P>],
50 ) -> impl Future<Output = Result<()>> + Send + 'a {
51 (**self).write_all(events)
52 }
53}
54
55pub trait DynWriter<P = Payload>: Send + Sync {
56 fn write_dyn<'a>(&'a self, event: &'a Event<P>) -> BoxFuture<'a, Result<()>>;
57 fn write_all_dyn<'a>(&'a self, events: &'a [Event<P>]) -> BoxFuture<'a, Result<()>>;
58}
59
60impl<T: Writer<P> + ?Sized, P: Send + Sync> DynWriter<P> for T {
61 fn write_dyn<'a>(&'a self, event: &'a Event<P>) -> BoxFuture<'a, Result<()>> {
62 Box::pin(<Self as Writer<P>>::write(self, event))
63 }
64 fn write_all_dyn<'a>(&'a self, events: &'a [Event<P>]) -> BoxFuture<'a, Result<()>> {
65 Box::pin(<Self as Writer<P>>::write_all(self, events))
66 }
67}
68
69pub type BoxWriter<P = Payload> = Box<dyn DynWriter<P>>;
70pub type ArcWriter<P = Payload> = Arc<dyn DynWriter<P>>;
71
72impl<P: Send + Sync> Writer<P> for dyn DynWriter<P> + '_ {
73 fn write<'a>(&'a self, event: &'a Event<P>) -> impl Future<Output = Result<()>> + Send + 'a {
74 DynWriter::write_dyn(self, event)
75 }
76 fn write_all<'a>(
77 &'a self,
78 events: &'a [Event<P>],
79 ) -> impl Future<Output = Result<()>> + Send + 'a {
80 DynWriter::write_all_dyn(self, events)
81 }
82}
83
84pub trait WriterExt<P = Payload>: Writer<P> + Sized + 'static
85where
86 P: Send + Sync,
87{
88 fn into_boxed(self) -> BoxWriter<P> {
89 Box::new(self)
90 }
91
92 fn into_arced(self) -> ArcWriter<P> {
93 Arc::new(self)
94 }
95}
96
97impl<T: Writer<P> + Sized + 'static, P: Send + Sync> WriterExt<P> for T {}
98
99pub mod batch;
100pub mod encode;
101pub mod fanout;
102pub mod filtered;
103pub mod flat_map;
104pub mod inspect;
105pub mod map;
106pub mod retry;
107pub mod timeout;
108
109pub use batch::{BatchWriter, BatchWriterConfig};
110pub use encode::{EncodeWriter, WriterTypedExt};
111pub use fanout::FanoutWriter;
112pub use filtered::FilteredWriter;
113pub use flat_map::{FlatMapWriter, TryFlatMapWriter};
114pub use inspect::{InspectWriter, InspectWriterHooks};
115pub use map::{MapWriter, TryMapWriter};
116pub use retry::{RetryWriter, RetryWriterConfig};
117pub use timeout::TimeoutWriter;
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 use std::sync::atomic::{AtomicUsize, Ordering};
124
125 struct CountingWriter {
126 writes: Arc<AtomicUsize>,
127 }
128
129 impl Writer for CountingWriter {
130 async fn write(&self, _: &Event) -> Result<()> {
131 self.writes.fetch_add(1, Ordering::SeqCst);
132 Ok(())
133 }
134 }
135
136 fn ev() -> Event {
137 Event::create(
138 "org",
139 "/x",
140 "thing.happened",
141 "thing-1",
142 crate::payload::Payload::from_string("p"),
143 )
144 .unwrap()
145 }
146
147 #[tokio::test]
148 async fn into_boxed_yields_dyn_writer() {
149 let writes = Arc::new(AtomicUsize::new(0));
150 let writer: BoxWriter = CountingWriter {
151 writes: Arc::clone(&writes),
152 }
153 .into_boxed();
154 writer.write(&ev()).await.unwrap();
155 writer.write_all(&[ev(), ev()]).await.unwrap();
156 assert_eq!(writes.load(Ordering::SeqCst), 3);
157 }
158
159 #[tokio::test]
160 async fn into_arced_yields_shared_writer() {
161 let writes = Arc::new(AtomicUsize::new(0));
162 let writer: ArcWriter = CountingWriter {
163 writes: Arc::clone(&writes),
164 }
165 .into_arced();
166 let clone = Arc::clone(&writer);
167 writer.write(&ev()).await.unwrap();
168 clone.write(&ev()).await.unwrap();
169 assert_eq!(writes.load(Ordering::SeqCst), 2);
170 }
171
172 #[tokio::test]
173 async fn box_blanket_passes_as_generic_writer() {
174 async fn take<W: Writer>(w: W, e: &Event) {
175 w.write(e).await.unwrap();
176 }
177 let writes = Arc::new(AtomicUsize::new(0));
178 let boxed: BoxWriter = CountingWriter {
179 writes: Arc::clone(&writes),
180 }
181 .into_boxed();
182 take(boxed, &ev()).await;
183 assert_eq!(writes.load(Ordering::SeqCst), 1);
184 }
185
186 #[tokio::test]
187 async fn arc_blanket_passes_as_generic_writer() {
188 async fn take<W: Writer>(w: W, e: &Event) {
189 w.write(e).await.unwrap();
190 }
191 let writes = Arc::new(AtomicUsize::new(0));
192 let arced: ArcWriter = CountingWriter {
193 writes: Arc::clone(&writes),
194 }
195 .into_arced();
196 take(arced, &ev()).await;
197 assert_eq!(writes.load(Ordering::SeqCst), 1);
198 }
199
200 #[derive(Debug, Clone, PartialEq, Eq)]
201 struct UserUpdated {
202 user_id: String,
203 }
204
205 fn typed_ev() -> Event<UserUpdated> {
206 Event::create(
207 "org",
208 "/users",
209 "user.updated",
210 "thing-1",
211 UserUpdated {
212 user_id: "u-1".to_owned(),
213 },
214 )
215 .unwrap()
216 }
217
218 struct TypedCountingWriter {
219 writes: Arc<AtomicUsize>,
220 }
221
222 impl Writer<UserUpdated> for TypedCountingWriter {
223 async fn write(&self, _: &Event<UserUpdated>) -> Result<()> {
224 self.writes.fetch_add(1, Ordering::SeqCst);
225 Ok(())
226 }
227 }
228
229 #[tokio::test]
230 async fn typed_writer_into_boxed_yields_dyn_writer() {
231 let writes = Arc::new(AtomicUsize::new(0));
232 let writer: BoxWriter<UserUpdated> = TypedCountingWriter {
233 writes: Arc::clone(&writes),
234 }
235 .into_boxed();
236
237 writer.write(&typed_ev()).await.unwrap();
238 assert_eq!(writes.load(Ordering::SeqCst), 1);
239 }
240}