Skip to main content

moonpool_core/
network.rs

1//! Network provider abstraction for simulation and real networking.
2//!
3//! This module provides trait-based networking that allows seamless swapping
4//! between real Tokio networking and simulated networking for testing.
5
6use futures::io::{AsyncRead, AsyncWrite};
7use std::io;
8#[cfg(feature = "tokio-providers")]
9use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
10
11/// Provider trait for creating network connections and listeners.
12///
13/// Clone allows sharing providers across multiple peers efficiently.
14pub trait NetworkProvider: Clone + Send + Sync + 'static {
15    /// The TCP stream type for this provider.
16    type TcpStream: AsyncRead + AsyncWrite + Unpin + Send + 'static;
17    /// The TCP listener type for this provider.
18    type TcpListener: TcpListenerTrait<TcpStream = Self::TcpStream> + 'static;
19
20    /// Create a TCP listener bound to the given address.
21    fn bind(
22        &self,
23        addr: &str,
24    ) -> impl std::future::Future<Output = io::Result<Self::TcpListener>> + Send;
25
26    /// Connect to a remote address.
27    fn connect(
28        &self,
29        addr: &str,
30    ) -> impl std::future::Future<Output = io::Result<Self::TcpStream>> + Send;
31}
32
33/// Trait for TCP listeners that can accept connections.
34pub trait TcpListenerTrait: Send + Sync + 'static {
35    /// The TCP stream type that this listener produces.
36    type TcpStream: AsyncRead + AsyncWrite + Unpin + Send + 'static;
37
38    /// Accept a single incoming connection.
39    fn accept(
40        &self,
41    ) -> impl std::future::Future<Output = io::Result<(Self::TcpStream, String)>> + Send;
42
43    /// Get the local address this listener is bound to.
44    ///
45    /// # Errors
46    ///
47    /// Returns an [`io::Error`] if the local address cannot be retrieved
48    /// from the underlying listener.
49    fn local_addr(&self) -> io::Result<String>;
50}
51
52/// Real Tokio networking implementation.
53///
54/// Wraps `tokio::net::TcpStream` with `tokio_util::compat::Compat` so it
55/// implements the runtime-agnostic `futures::io::AsyncRead + AsyncWrite` traits
56/// required by [`NetworkProvider`].
57#[cfg(feature = "tokio-providers")]
58#[derive(Debug, Clone, Default)]
59pub struct TokioNetworkProvider;
60
61#[cfg(feature = "tokio-providers")]
62impl TokioNetworkProvider {
63    /// Create a new Tokio network provider.
64    #[must_use]
65    pub fn new() -> Self {
66        Self
67    }
68}
69
70#[cfg(feature = "tokio-providers")]
71impl NetworkProvider for TokioNetworkProvider {
72    type TcpStream = Compat<tokio::net::TcpStream>;
73    type TcpListener = TokioTcpListener;
74
75    async fn bind(&self, addr: &str) -> io::Result<Self::TcpListener> {
76        let listener = tokio::net::TcpListener::bind(addr).await?;
77        Ok(TokioTcpListener { inner: listener })
78    }
79
80    async fn connect(&self, addr: &str) -> io::Result<Self::TcpStream> {
81        Ok(tokio::net::TcpStream::connect(addr).await?.compat())
82    }
83}
84
85/// Wrapper for Tokio `TcpListener` to implement our trait.
86#[cfg(feature = "tokio-providers")]
87#[derive(Debug)]
88pub struct TokioTcpListener {
89    inner: tokio::net::TcpListener,
90}
91
92#[cfg(feature = "tokio-providers")]
93impl TcpListenerTrait for TokioTcpListener {
94    type TcpStream = Compat<tokio::net::TcpStream>;
95
96    async fn accept(&self) -> io::Result<(Self::TcpStream, String)> {
97        let (stream, addr) = self.inner.accept().await?;
98        Ok((stream.compat(), addr.to_string()))
99    }
100
101    fn local_addr(&self) -> io::Result<String> {
102        Ok(self.inner.local_addr()?.to_string())
103    }
104}