ruststream_zeromq/
queue.rs1use std::sync::Arc;
4
5use futures::Stream;
6use ruststream::{
7 Broker, ConnectedBroker, DefaultPublish, DescribeServer, OutgoingMessage, PairError,
8 PublishPolicy, Publisher, ServerSpec, Subscribe, Subscriber,
9};
10use tokio::sync::{Mutex, OnceCell, mpsc};
11use zeromq::prelude::*;
12use zeromq::{PullSocket, PushSocket};
13
14use crate::common::{DriverHandle, Lifecycle, SharedLifecycle, send_with_retry};
15use crate::endpoint::ZmqEndpoint;
16use crate::error::ZmqError;
17use crate::message::ZmqMessage;
18use crate::wire;
19
20#[derive(Debug, Clone)]
32#[must_use]
33pub struct ZmqQueue {
34 endpoint: ZmqEndpoint,
35 cell: Arc<OnceCell<SharedLifecycle>>,
36}
37
38impl ZmqQueue {
39 pub fn new(endpoint: ZmqEndpoint) -> Self {
41 Self {
42 endpoint,
43 cell: Arc::new(OnceCell::new()),
44 }
45 }
46
47 #[must_use]
49 pub fn publisher(&self) -> ZmqQueuePublisher {
50 ZmqQueuePublisher {
51 cell: Arc::clone(&self.cell),
52 push: Arc::new(Mutex::new(None)),
53 }
54 }
55}
56
57impl Broker for ZmqQueue {
58 type Error = ZmqError;
59 type Connected = ConnectedZmqQueue;
60
61 async fn connect(self) -> Result<Self::Connected, Self::Error> {
62 let lifecycle = self
63 .cell
64 .get_or_try_init(async || {
65 self.endpoint.validate()?;
66 Ok::<_, ZmqError>(Arc::new(Lifecycle::new(self.endpoint.clone())))
67 })
68 .await?
69 .clone();
70 Ok(ConnectedZmqQueue {
71 lifecycle,
72 cell: self.cell,
73 })
74 }
75}
76
77impl DescribeServer for ZmqQueue {
78 fn describe_server(&self) -> ServerSpec {
79 ServerSpec::new(self.endpoint.address(), "zeromq")
80 }
81}
82
83#[derive(Debug)]
85pub struct ConnectedZmqQueue {
86 lifecycle: SharedLifecycle,
87 cell: Arc<OnceCell<SharedLifecycle>>,
88}
89
90impl ConnectedZmqQueue {
91 #[must_use]
94 pub fn bound_address(&self) -> Option<String> {
95 self.lifecycle.resolved.get().cloned()
96 }
97
98 #[must_use]
100 pub fn publisher(&self) -> ZmqQueuePublisher {
101 ZmqQueuePublisher {
102 cell: Arc::clone(&self.cell),
103 push: Arc::new(Mutex::new(None)),
104 }
105 }
106}
107
108impl ConnectedBroker for ConnectedZmqQueue {
109 type Error = ZmqError;
110 type Closed = ();
111
112 async fn shutdown(self) -> Result<(), Self::Error> {
113 self.lifecycle
114 .closed
115 .store(true, std::sync::atomic::Ordering::Release);
116 Ok(())
117 }
118}
119
120impl Subscribe for ConnectedZmqQueue {
121 type Subscriber = ZmqSubscriber;
122
123 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
124 self.lifecycle.ensure_open()?;
125 let mut socket = PullSocket::new();
126 self.lifecycle.attach_receiver(&mut socket).await?;
127
128 let (tx, rx) = mpsc::unbounded_channel();
129 let task = tokio::spawn(async move {
130 loop {
131 match socket.recv().await {
132 Ok(message) => {
133 let item =
134 wire::decode(message).map(|(name, headers, payload)| ZmqMessage {
135 name,
136 headers,
137 payload,
138 });
139 if tx.send(item).is_err() {
140 break;
141 }
142 }
143 Err(err) => {
144 if tx.send(Err(ZmqError::Receive(err.to_string()))).is_err() {
145 break;
146 }
147 }
148 }
149 }
150 });
151 Ok(ZmqSubscriber {
152 name: name.to_owned(),
153 rx,
154 _driver: DriverHandle { task },
155 })
156 }
157}
158
159pub struct ZmqSubscriber {
161 name: String,
162 rx: mpsc::UnboundedReceiver<Result<ZmqMessage, ZmqError>>,
163 _driver: DriverHandle,
164}
165
166impl std::fmt::Debug for ZmqSubscriber {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 f.debug_struct("ZmqSubscriber")
169 .field("name", &self.name)
170 .finish_non_exhaustive()
171 }
172}
173
174impl ZmqSubscriber {
175 pub(crate) fn from_parts(
176 name: String,
177 rx: mpsc::UnboundedReceiver<Result<ZmqMessage, ZmqError>>,
178 driver: DriverHandle,
179 ) -> Self {
180 Self {
181 name,
182 rx,
183 _driver: driver,
184 }
185 }
186}
187
188impl Subscriber for ZmqSubscriber {
189 type Message = ZmqMessage;
190 type Error = ZmqError;
191
192 fn stream(&mut self) -> impl Stream<Item = Result<ZmqMessage, ZmqError>> + Send + '_ {
193 futures::stream::poll_fn(move |cx| self.rx.poll_recv(cx))
197 }
198}
199
200#[derive(Clone)]
202pub struct ZmqQueuePublisher {
203 cell: Arc<OnceCell<SharedLifecycle>>,
204 push: Arc<Mutex<Option<PushSocket>>>,
205}
206
207impl std::fmt::Debug for ZmqQueuePublisher {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 f.debug_struct("ZmqQueuePublisher").finish_non_exhaustive()
210 }
211}
212
213impl Publisher for ZmqQueuePublisher {
214 type Error = ZmqError;
215
216 #[allow(clippy::significant_drop_tightening)]
219 async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
220 let lifecycle = self.cell.get().ok_or(ZmqError::NotConnected)?;
221 lifecycle.ensure_open()?;
222 let mut push = self.push.lock().await;
223 if push.is_none() {
224 let mut socket = PushSocket::new();
225 lifecycle.attach_sender(&mut socket).await?;
226 *push = Some(socket);
227 }
228 let socket = push.as_mut().expect("just attached");
229 send_with_retry(
230 socket,
231 msg.name(),
232 wire::encode(msg.name(), msg.headers(), msg.payload()),
233 )
234 .await
235 }
236}
237
238#[derive(Debug, Clone, Copy, Default)]
249#[must_use]
250pub struct ZmqQueuePublish;
251
252impl PublishPolicy<ConnectedZmqQueue> for ZmqQueuePublish {
253 type Live = ZmqQueuePublisher;
254
255 async fn pair(self, connected: &ConnectedZmqQueue) -> Result<Self::Live, PairError> {
256 Ok(connected.publisher())
257 }
258}
259
260impl DefaultPublish for ConnectedZmqQueue {
261 type Policy = ZmqQueuePublish;
262}