Skip to main content

monocoque/zmq/
rep.rs

1//! REP socket implementation.
2
3use super::common::channel_to_io_error;
4use bytes::Bytes;
5use monocoque_core::monitor::{SocketEventSender, SocketMonitor, create_monitor};
6use monocoque_core::options::SocketOptions;
7use monocoque_core::rt::{TcpListener, TcpStream};
8use monocoque_zmtp::SocketType;
9use monocoque_zmtp::rep::RepSocket as InternalRep;
10use std::io;
11
12/// A REP socket for synchronous reply patterns.
13///
14/// REP sockets enforce strict alternation between receive and send:
15/// - Must call `recv()` to get a request
16/// - Must call `send()` to reply before next `recv()`
17/// - Automatically handles routing envelopes
18///
19/// They're used for:
20/// - Synchronous RPC servers
21/// - Request-reply protocols
22/// - Service endpoints
23///
24/// ## ZeroMQ Compatibility
25///
26/// Compatible with `zmq::REQ` and `zmq::REP` sockets from libzmq.
27///
28/// ## Example
29///
30/// ```rust,no_run
31/// use monocoque::zmq::RepSocket;
32/// use monocoque_core::rt::TcpListener;
33/// use bytes::Bytes;
34///
35/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
36/// // Bind and accept
37/// let listener = TcpListener::bind("127.0.0.1:5555").await?;
38/// let (stream, _) = listener.accept().await?;
39/// let mut socket = RepSocket::from_tcp(stream).await?;
40///
41/// loop {
42///     // Receive request
43///     if let Ok(Some(request)) = socket.recv().await {
44///         println!("Got request: {:?}", request);
45///         
46///         // Send reply
47///         socket.send(vec![Bytes::from("REPLY")]).await?;
48///     }
49/// }
50/// # }
51/// ```
52pub struct RepSocket<S = TcpStream>
53where
54    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
55{
56    inner: InternalRep<S>,
57    monitor: Option<SocketEventSender>,
58}
59
60impl RepSocket {
61    /// Bind to `addr`, accept one connection, and return a ready REP socket.
62    ///
63    /// Returns the `TcpListener` so the caller can accept further connections.
64    ///
65    /// # Example
66    ///
67    /// ```rust,no_run
68    /// use monocoque::zmq::RepSocket;
69    /// use bytes::Bytes;
70    ///
71    /// # async fn example() -> std::io::Result<()> {
72    /// let (_listener, mut socket) = RepSocket::bind("127.0.0.1:5555").await?;
73    /// if let Ok(Some(req)) = socket.recv().await {
74    ///     socket.send(vec![Bytes::from("PONG")]).await?;
75    /// }
76    /// # Ok(())
77    /// # }
78    /// ```
79    pub async fn bind(
80        addr: impl monocoque_core::rt::ToSocketAddrs,
81    ) -> io::Result<(TcpListener, Self)> {
82        let listener = TcpListener::bind(addr).await?;
83        let (stream, _) = listener.accept().await?;
84        let socket = Self::from_tcp(stream).await?;
85        Ok((listener, socket))
86    }
87
88    /// Bind with custom socket options.
89    pub async fn bind_with_options(
90        addr: impl monocoque_core::rt::ToSocketAddrs,
91        options: SocketOptions,
92    ) -> io::Result<(TcpListener, Self)> {
93        let listener = TcpListener::bind(addr).await?;
94        let (stream, _) = listener.accept().await?;
95        let socket = Self::from_tcp_with_options(stream, options).await?;
96        Ok((listener, socket))
97    }
98
99    /// Create a REP socket from an existing TCP stream.
100    ///
101    /// Create a REP socket from a TCP stream with TCP_NODELAY enabled.
102    pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
103        Ok(Self {
104            inner: InternalRep::from_tcp(stream).await?,
105            monitor: None,
106        })
107    }
108
109    /// Create a REP socket from a TCP stream with custom options.
110    pub async fn from_tcp_with_options(
111        stream: TcpStream,
112        options: monocoque_core::options::SocketOptions,
113    ) -> io::Result<Self> {
114        Ok(Self {
115            inner: InternalRep::with_options(stream, options).await?,
116            monitor: None,
117        })
118    }
119
120    /// Create a REP socket from any stream with custom options.
121    pub async fn with_options<Stream>(
122        stream: Stream,
123        options: monocoque_core::options::SocketOptions,
124    ) -> io::Result<RepSocket<Stream>>
125    where
126        Stream: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
127    {
128        Ok(RepSocket {
129            inner: InternalRep::with_options(stream, options).await?,
130            monitor: None,
131        })
132    }
133}
134
135// Generic impl - works with any stream type
136impl<S> RepSocket<S>
137where
138    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
139{
140    /// Enable monitoring for this socket.
141    ///
142    /// Returns a receiver for socket lifecycle events.
143    pub fn monitor(&mut self) -> SocketMonitor {
144        let (sender, receiver) = create_monitor();
145        self.monitor = Some(sender);
146        receiver
147    }
148
149    /// Receive a request message.
150    ///
151    /// This blocks until a request is received. The routing envelope is
152    /// automatically extracted and stored for the subsequent `send()` call.
153    ///
154    /// # Returns
155    ///
156    /// - `Some(msg)` - Received a request (content only, envelope stripped)
157    /// - `None` - Connection closed gracefully or error occurred
158    ///
159    /// # Example
160    ///
161    /// ```rust,no_run
162    /// use monocoque::zmq::RepSocket;
163    ///
164    /// # async fn example(socket: &mut RepSocket) -> std::io::Result<()> {
165    /// if let Ok(Some(request)) = socket.recv().await {
166    ///     for (i, frame) in request.iter().enumerate() {
167    ///         println!("Frame {}: {:?}", i, frame);
168    ///     }
169    /// }
170    /// # Ok(())
171    /// # }
172    /// ```
173    pub async fn recv(&mut self) -> io::Result<Option<Vec<Bytes>>> {
174        self.inner.recv().await
175    }
176
177    /// Get the socket type.
178    ///
179    /// # ZeroMQ Compatibility
180    ///
181    /// Corresponds to `ZMQ_TYPE` (16) option.
182    #[inline]
183    pub const fn socket_type() -> SocketType {
184        SocketType::Rep
185    }
186
187    /// Get the endpoint this socket is connected/bound to, if available.
188    ///
189    /// Returns `None` if the socket was created from a raw stream.
190    ///
191    /// # ZeroMQ Compatibility
192    ///
193    /// Corresponds to `ZMQ_LAST_ENDPOINT` (32) option.
194    #[inline]
195    pub fn last_endpoint(&self) -> Option<&monocoque_core::endpoint::Endpoint> {
196        self.inner.last_endpoint()
197    }
198
199    /// Check if the last received message has more frames coming.
200    ///
201    /// Returns `true` if there are more frames in the current multipart message.
202    ///\n    /// # ZeroMQ Compatibility
203    ///
204    /// Corresponds to `ZMQ_RCVMORE` (13) option.
205    #[inline]
206    pub fn has_more(&self) -> bool {
207        self.inner.has_more()
208    }
209
210    /// Get the event state of the socket.
211    ///
212    /// Returns a bitmask indicating ready-to-receive and ready-to-send states.
213    ///
214    /// # Returns
215    ///
216    /// - `1` (POLLIN) - Socket is ready to receive
217    /// - `2` (POLLOUT) - Socket is ready to send
218    /// - `3` (POLLIN | POLLOUT) - Socket is ready for both
219    ///
220    /// # ZeroMQ Compatibility
221    ///
222    /// Corresponds to `ZMQ_EVENTS` (15) option.
223    #[inline]
224    pub fn events(&self) -> u32 {
225        self.inner.events()
226    }
227
228    /// Send a reply message.
229    ///
230    /// This must be called after `recv()` and automatically uses the stored
231    /// routing envelope from the request.
232    ///
233    /// # Arguments
234    ///
235    /// * `msg` - Vector of message frames (parts) to send as reply
236    ///
237    /// # Errors
238    ///
239    /// Returns an error if:
240    /// - Called without first calling `recv()`
241    /// - The underlying connection is closed
242    ///
243    /// # Example
244    ///
245    /// ```rust,no_run
246    /// use monocoque::zmq::RepSocket;
247    /// use bytes::Bytes;
248    ///
249    /// # async fn example(socket: &mut RepSocket) -> std::io::Result<()> {
250    /// // Send single-part reply
251    /// socket.send(vec![Bytes::from("OK")]).await?;
252    ///
253    /// // Send multi-part reply
254    /// socket.send(vec![
255    ///     Bytes::from("Status: OK"),
256    ///     Bytes::from("Data: ..."),
257    /// ]).await?;
258    /// # Ok(())
259    /// # }
260    /// ```
261    pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
262        channel_to_io_error(self.inner.send(msg).await)
263    }
264
265    /// Get a mutable reference to this socket's options.
266    #[inline]
267    pub fn options_mut(&mut self) -> &mut SocketOptions {
268        self.inner.options_mut()
269    }
270}
271
272// Unix-specific impl for IPC support
273#[cfg(unix)]
274impl RepSocket<monocoque_core::rt::UnixStream> {
275    /// Create a REP socket from an existing Unix domain socket stream (IPC).
276    pub async fn from_unix_stream(stream: monocoque_core::rt::UnixStream) -> io::Result<Self> {
277        Ok(Self {
278            inner: InternalRep::new(stream).await?,
279            monitor: None,
280        })
281    }
282
283    /// Create a REP socket from an existing Unix stream with custom options.
284    ///
285    /// This method provides full control over socket behavior through SocketOptions.
286    pub async fn from_unix_stream_with_options(
287        stream: monocoque_core::rt::UnixStream,
288        options: monocoque_core::options::SocketOptions,
289    ) -> io::Result<Self> {
290        Ok(Self {
291            inner: InternalRep::with_options(stream, options).await?,
292            monitor: None,
293        })
294    }
295}