Skip to main content

monocoque_zmtp/
socket_trait.rs

1//! Trait-based socket API for polymorphic socket handling.
2//!
3//! This module provides a generic `Socket` trait that enables working with
4//! different socket types in a uniform way, particularly useful for:
5//! - Generic proxy implementations
6//! - Testing and mocking
7//! - Dynamic socket type selection
8//! - Library APIs that work with any socket type
9
10use bytes::Bytes;
11use std::io;
12
13use crate::SocketType;
14
15/// Generic socket trait for polymorphic handling of different socket types.
16///
17/// All ZeroMQ socket types (DEALER, ROUTER, REQ, REP, PAIR, PUSH, PULL, SUB, XSUB, XPUB, PUB)
18/// implement this trait, enabling:
19/// - Generic functions that work with any socket type
20/// - Proxy implementations (e.g., `proxy<F, B>()` where F, B: Socket)
21/// - Testing with mock sockets
22/// - Runtime socket type selection
23///
24/// # Examples
25///
26/// ```no_run
27/// use monocoque_zmtp::{Socket, DealerSocket, RouterSocket};
28/// use std::io;
29///
30/// async fn forward_messages<S1, S2>(from: &mut S1, to: &mut S2) -> io::Result<()>
31/// where
32///     S1: Socket,
33///     S2: Socket,
34/// {
35///     while let Some(msg) = from.recv().await? {
36///         to.send(msg).await?;
37///     }
38///     Ok(())
39/// }
40/// ```
41// Native async-fn-in-trait rather than `#[async_trait]`: this runtime is
42// thread-per-core and every future is intentionally `!Send`, so the boxing
43// async_trait added on each send/recv bought nothing. AFIT drops that per-call
44// heap allocation. The trait is never used as `dyn Socket` (callers are
45// generic), so object safety is not a concern; allow the public-AFIT lint whose
46// Send-bound warning does not apply to this deliberately single-threaded API.
47#[allow(async_fn_in_trait)]
48pub trait Socket {
49    /// Send a multipart message on the socket.
50    ///
51    /// # Arguments
52    ///
53    /// * `msg` - Multipart message as a vector of frames
54    ///
55    /// # Returns
56    ///
57    /// - `Ok(())` - Message sent successfully
58    /// - `Err(io::Error)` - Send failed (timeout, disconnection, etc.)
59    async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()>;
60
61    /// Receive a multipart message from the socket.
62    ///
63    /// # Returns
64    ///
65    /// - `Ok(Some(msg))` - Message received successfully
66    /// - `Ok(None)` - No message available (non-blocking mode)
67    /// - `Err(io::Error)` - Receive failed (timeout, disconnection, etc.)
68    async fn recv(&mut self) -> io::Result<Option<Vec<Bytes>>>;
69
70    /// Get the socket type.
71    ///
72    /// Returns the ZeroMQ socket type enum (DEALER, ROUTER, REQ, etc.).
73    fn socket_type(&self) -> SocketType;
74
75    /// Check if socket has more message frames pending.
76    ///
77    /// Equivalent to ZMQ_RCVMORE option. Returns true if the last recv()
78    /// operation received a partial message with more frames to follow.
79    fn has_more(&self) -> bool {
80        // Default implementation - sockets can override if they track this
81        false
82    }
83}
84
85/// Macro to implement the Socket trait for socket types with standard send/recv methods.
86///
87/// This macro generates boilerplate trait implementations for socket types that follow
88/// the standard pattern of having `send(&mut self, Vec<Bytes>)` and `recv(&mut self)` methods.
89///
90/// # Usage
91///
92/// ```ignore
93/// impl_socket_trait!(DealerSocket<S>, SocketType::Dealer);
94/// ```
95#[macro_export]
96macro_rules! impl_socket_trait {
97    ($socket_type:ty, $zmq_type:expr) => {
98        impl<S> $crate::Socket for $socket_type
99        where
100            S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin + 'static,
101        {
102            async fn send(&mut self, msg: Vec<bytes::Bytes>) -> std::io::Result<()> {
103                self.send(msg).await
104            }
105
106            async fn recv(&mut self) -> std::io::Result<Option<Vec<bytes::Bytes>>> {
107                self.recv().await
108            }
109
110            fn socket_type(&self) -> $crate::SocketType {
111                $zmq_type
112            }
113        }
114    };
115}
116
117/// Implement the `Socket` trait for a receive-only socket (PULL, SUB, XSUB).
118///
119/// These types have no inherent `send`, so `Socket::send` must not forward to
120/// `self.send` - with native async-fn-in-trait that would resolve to this very
121/// trait method and recurse. It returns `Unsupported` instead, matching how PUB
122/// reports the reverse direction.
123#[macro_export]
124macro_rules! impl_socket_trait_recv_only {
125    ($socket_type:ty, $zmq_type:expr) => {
126        impl<S> $crate::Socket for $socket_type
127        where
128            S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin + 'static,
129        {
130            async fn send(&mut self, _msg: Vec<bytes::Bytes>) -> std::io::Result<()> {
131                Err(std::io::Error::new(
132                    std::io::ErrorKind::Unsupported,
133                    "this socket type does not support send",
134                ))
135            }
136
137            async fn recv(&mut self) -> std::io::Result<Option<Vec<bytes::Bytes>>> {
138                self.recv().await
139            }
140
141            fn socket_type(&self) -> $crate::SocketType {
142                $zmq_type
143            }
144        }
145    };
146}
147
148/// Implement the `Socket` trait for a send-only socket (PUSH).
149///
150/// PUSH has no inherent `recv`, so `Socket::recv` returns `Unsupported` rather
151/// than forwarding to itself and recursing under native async-fn-in-trait.
152#[macro_export]
153macro_rules! impl_socket_trait_send_only {
154    ($socket_type:ty, $zmq_type:expr) => {
155        impl<S> $crate::Socket for $socket_type
156        where
157            S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin + 'static,
158        {
159            async fn send(&mut self, msg: Vec<bytes::Bytes>) -> std::io::Result<()> {
160                self.send(msg).await
161            }
162
163            async fn recv(&mut self) -> std::io::Result<Option<Vec<bytes::Bytes>>> {
164                Err(std::io::Error::new(
165                    std::io::ErrorKind::Unsupported,
166                    "this socket type does not support recv",
167                ))
168            }
169
170            fn socket_type(&self) -> $crate::SocketType {
171                $zmq_type
172            }
173        }
174    };
175}