ruststream_zeromq/
fanout.rs1use std::sync::Arc;
8
9use ruststream::{
10 Broker, ConnectedBroker, DefaultPublish, DescribeServer, OutgoingMessage, PairError,
11 PublishPolicy, Publisher, ServerSpec, Subscribe,
12};
13use tokio::sync::{Mutex, OnceCell, mpsc};
14use zeromq::prelude::*;
15use zeromq::{PubSocket, SubSocket};
16
17use crate::common::{DriverHandle, Lifecycle, SharedLifecycle, send_with_retry};
18use crate::endpoint::ZmqEndpoint;
19use crate::error::ZmqError;
20use crate::message::ZmqMessage;
21use crate::queue::ZmqSubscriber;
22use crate::wire;
23
24#[derive(Debug, Clone)]
36#[must_use]
37pub struct ZmqFanout {
38 endpoint: ZmqEndpoint,
39 cell: Arc<OnceCell<SharedLifecycle>>,
40}
41
42impl ZmqFanout {
43 pub fn new(endpoint: ZmqEndpoint) -> Self {
45 Self {
46 endpoint,
47 cell: Arc::new(OnceCell::new()),
48 }
49 }
50
51 #[must_use]
53 pub fn publisher(&self) -> ZmqFanoutPublisher {
54 ZmqFanoutPublisher {
55 cell: Arc::clone(&self.cell),
56 socket: Arc::new(Mutex::new(None)),
57 }
58 }
59}
60
61impl Broker for ZmqFanout {
62 type Error = ZmqError;
63 type Connected = ConnectedZmqFanout;
64
65 async fn connect(self) -> Result<Self::Connected, Self::Error> {
66 let lifecycle = self
67 .cell
68 .get_or_try_init(async || {
69 self.endpoint.validate()?;
70 Ok::<_, ZmqError>(Arc::new(Lifecycle::new(self.endpoint.clone())))
71 })
72 .await?
73 .clone();
74 Ok(ConnectedZmqFanout {
75 lifecycle,
76 cell: self.cell,
77 })
78 }
79}
80
81impl DescribeServer for ZmqFanout {
82 fn describe_server(&self) -> ServerSpec {
83 ServerSpec::new(self.endpoint.address(), "zeromq")
84 }
85}
86
87#[derive(Debug)]
89pub struct ConnectedZmqFanout {
90 lifecycle: SharedLifecycle,
91 cell: Arc<OnceCell<SharedLifecycle>>,
92}
93
94impl ConnectedZmqFanout {
95 #[must_use]
98 pub fn bound_address(&self) -> Option<String> {
99 self.lifecycle.resolved.get().cloned()
100 }
101
102 #[must_use]
104 pub fn publisher(&self) -> ZmqFanoutPublisher {
105 ZmqFanoutPublisher {
106 cell: Arc::clone(&self.cell),
107 socket: Arc::new(Mutex::new(None)),
108 }
109 }
110}
111
112impl ConnectedBroker for ConnectedZmqFanout {
113 type Error = ZmqError;
114 type Closed = ();
115
116 async fn shutdown(self) -> Result<(), Self::Error> {
117 self.lifecycle
118 .closed
119 .store(true, std::sync::atomic::Ordering::Release);
120 Ok(())
121 }
122}
123
124impl Subscribe for ConnectedZmqFanout {
125 type Subscriber = ZmqSubscriber;
126
127 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
128 self.lifecycle.ensure_open()?;
129 let mut socket = SubSocket::new();
130 self.lifecycle.attach_receiver(&mut socket).await?;
131 socket
134 .subscribe(name)
135 .await
136 .map_err(|e| ZmqError::Receive(e.to_string()))?;
137
138 let (tx, rx) = mpsc::unbounded_channel();
139 let task = tokio::spawn(async move {
140 loop {
141 match socket.recv().await {
142 Ok(message) => {
143 let item =
144 wire::decode(message).map(|(name, headers, payload)| ZmqMessage {
145 name,
146 headers,
147 payload,
148 });
149 if tx.send(item).is_err() {
150 break;
151 }
152 }
153 Err(err) => {
154 if tx.send(Err(ZmqError::Receive(err.to_string()))).is_err() {
155 break;
156 }
157 }
158 }
159 }
160 });
161 Ok(ZmqSubscriber::from_parts(
162 name.to_owned(),
163 rx,
164 DriverHandle { task },
165 ))
166 }
167}
168
169#[derive(Clone)]
174pub struct ZmqFanoutPublisher {
175 cell: Arc<OnceCell<SharedLifecycle>>,
176 socket: Arc<Mutex<Option<PubSocket>>>,
177}
178
179impl std::fmt::Debug for ZmqFanoutPublisher {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 f.debug_struct("ZmqFanoutPublisher").finish_non_exhaustive()
182 }
183}
184
185impl Publisher for ZmqFanoutPublisher {
186 type Error = ZmqError;
187
188 #[allow(clippy::significant_drop_tightening)]
191 async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
192 let lifecycle = self.cell.get().ok_or(ZmqError::NotConnected)?;
193 lifecycle.ensure_open()?;
194 let mut guard = self.socket.lock().await;
195 if guard.is_none() {
196 let mut socket = PubSocket::new();
197 lifecycle.attach_sender(&mut socket).await?;
198 *guard = Some(socket);
199 }
200 let socket = guard.as_mut().expect("just attached");
201 send_with_retry(
204 socket,
205 msg.name(),
206 wire::encode(msg.name(), msg.headers(), msg.payload()),
207 )
208 .await
209 }
210}
211
212#[derive(Debug, Clone, Copy, Default)]
223#[must_use]
224pub struct ZmqFanoutPublish;
225
226impl PublishPolicy<ConnectedZmqFanout> for ZmqFanoutPublish {
227 type Live = ZmqFanoutPublisher;
228
229 async fn pair(self, connected: &ConnectedZmqFanout) -> Result<Self::Live, PairError> {
230 Ok(connected.publisher())
231 }
232}
233
234impl DefaultPublish for ConnectedZmqFanout {
235 type Policy = ZmqFanoutPublish;
236}