chat_core/network/
mod.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::future::Future;
4
5// ------------------------------------------------------------------
6// Typed wrappers for network identifiers
7// ------------------------------------------------------------------
8
9/// A peer identifier string (e.g., a libp2p PeerId).
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct PeerId(pub String);
12
13impl PeerId {
14    pub fn as_str(&self) -> &str {
15        &self.0
16    }
17}
18
19impl fmt::Display for PeerId {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        self.0.fmt(f)
22    }
23}
24
25impl AsRef<str> for PeerId {
26    fn as_ref(&self) -> &str {
27        &self.0
28    }
29}
30
31impl From<String> for PeerId {
32    fn from(s: String) -> Self {
33        PeerId(s)
34    }
35}
36
37impl From<&str> for PeerId {
38    fn from(s: &str) -> Self {
39        PeerId(s.to_string())
40    }
41}
42
43/// A multiaddr string (dialable network address).
44#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub struct Multiaddr(pub String);
46
47impl Multiaddr {
48    pub fn as_str(&self) -> &str {
49        &self.0
50    }
51}
52
53impl fmt::Display for Multiaddr {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        self.0.fmt(f)
56    }
57}
58
59impl AsRef<str> for Multiaddr {
60    fn as_ref(&self) -> &str {
61        &self.0
62    }
63}
64
65impl From<String> for Multiaddr {
66    fn from(s: String) -> Self {
67        Multiaddr(s)
68    }
69}
70
71impl From<&str> for Multiaddr {
72    fn from(s: &str) -> Self {
73        Multiaddr(s.to_string())
74    }
75}
76
77/// An opaque request identifier returned by `NetworkNode::send_message`.
78#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub struct RequestId(pub String);
80
81impl RequestId {
82    pub fn as_str(&self) -> &str {
83        &self.0
84    }
85}
86
87impl fmt::Display for RequestId {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        self.0.fmt(f)
90    }
91}
92
93impl AsRef<str> for RequestId {
94    fn as_ref(&self) -> &str {
95        &self.0
96    }
97}
98
99impl From<String> for RequestId {
100    fn from(s: String) -> Self {
101        RequestId(s)
102    }
103}
104
105impl From<&str> for RequestId {
106    fn from(s: &str) -> Self {
107        RequestId(s.to_string())
108    }
109}
110
111// ------------------------------------------------------------------
112// Room identifier
113// ------------------------------------------------------------------
114
115/// Opaque room identifier. The application only sees strings.
116/// The network backend decides how to map a room name to a provider key.
117#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
118pub struct RoomId(pub String);
119
120impl RoomId {
121    pub fn as_str(&self) -> &str {
122        &self.0
123    }
124}
125
126impl fmt::Display for RoomId {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        self.0.fmt(f)
129    }
130}
131
132impl AsRef<str> for RoomId {
133    fn as_ref(&self) -> &str {
134        &self.0
135    }
136}
137
138impl From<String> for RoomId {
139    fn from(s: String) -> Self {
140        RoomId(s)
141    }
142}
143
144impl From<&str> for RoomId {
145    fn from(s: &str) -> Self {
146        RoomId(s.to_string())
147    }
148}
149
150// ------------------------------------------------------------------
151// Ephemeral signaling message
152// ------------------------------------------------------------------
153
154/// Ephemeral signaling message exchanged over gossipsub to help peers
155/// discover each other's dialable addresses without sending chat messages.
156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
157pub struct HelloMessage {
158    pub peer_id: String,
159    pub circuit_address: Option<String>,
160    pub web_rtc_address: Option<String>,
161}
162
163pub mod handlers;
164
165#[cfg(test)]
166pub mod mock;
167
168/// Result type for network operations.
169pub type NetworkResult<T> = crate::error::ChatResult<T>;
170
171/// Trait implemented by every P2P backend.
172pub trait NetworkNode {
173    /// Return the local peer/node/endpoint ID.
174    fn local_id(&self) -> PeerId;
175
176    /// Dial a peer by an opaque address string (multiaddr, endpoint ticket, etc.).
177    fn dial(&mut self, addr: &Multiaddr) -> NetworkResult<()>;
178
179    /// Join a room/topic. The backend maps the room name to its internal topic mechanism.
180    fn join_room(&mut self, room: &RoomId) -> NetworkResult<()>;
181
182    /// Publish a message to the room topic (pubsub / gossip).
183    /// All room members receive this via BroadcastReceived.
184    fn publish_message(&mut self, data: Vec<u8>) -> NetworkResult<()>;
185
186    /// Send a message to a specific peer. Returns an opaque request ID.
187    fn send_message(&mut self, peer_id: &PeerId, data: Vec<u8>) -> NetworkResult<RequestId>;
188
189    /// Respond to a received message/request.
190    fn send_response(&mut self, request_id: &RequestId, data: Vec<u8>) -> NetworkResult<()>;
191
192    /// Get currently known dialable addresses.
193    fn dial_addrs(&self) -> Vec<Multiaddr>;
194
195    /// Bootstrap the network (DHT, relay, etc.).
196    fn bootstrap(&mut self) -> NetworkResult<()>;
197
198    /// Return raw network stats.
199    fn raw_stats(&self) -> crate::stats::RawStats;
200
201    /// Return connected peer IDs (excluding self).
202    fn connected_peers(&self) -> Vec<PeerId>;
203
204    /// Return whether we have an in-flight dial to the given peer.
205    fn is_dialing(&self, peer_id: &PeerId) -> bool;
206
207    /// Return the next network event.
208    fn next_event(&mut self) -> impl Future<Output = Option<NetworkEvent>>;
209}
210
211/// Events emitted by the network backend, translated from raw P2P events.
212#[derive(Debug, Clone)]
213pub enum NetworkEvent {
214    PeerConnected { peer_id: PeerId },
215    PeerDisconnected { peer_id: PeerId },
216    /// A direct message was received from a peer (request-response).
217    /// request_id is set for request-response protocols so the coordinator can reply.
218    /// For simple direct sends without a response channel, request_id may be None.
219    MessageReceived { peer_id: PeerId, request_id: Option<RequestId>, data: Vec<u8> },
220    /// A broadcast message was received from the room topic (pubsub / gossip).
221    BroadcastReceived { peer_id: PeerId, data: Vec<u8> },
222    NewListenAddr { addr: Multiaddr },
223    ExternalAddrConfirmed { addr: Multiaddr },
224    /// A new peer joined the active room topic.
225    RoomPeerJoined { peer_id: PeerId },
226    /// A peer left the active room topic.
227    RoomPeerLeft { peer_id: PeerId },
228    StatsUpdated { total_peers: usize },
229}