1use crate::error::SdkError;
4use crate::network::{MemoryTransport, NetworkTransport, Peer, PeerId};
5use crate::session::Session;
6use parking_lot::RwLock;
7use std::collections::HashMap;
8use std::sync::Arc;
9
10#[derive(Clone, Debug)]
12pub struct ClientConfig {
13 pub user_name: String,
15 pub auto_reconnect: bool,
17 pub max_reconnect_attempts: u32,
19}
20
21impl Default for ClientConfig {
22 fn default() -> Self {
23 Self {
24 user_name: "Anonymous".to_string(),
25 auto_reconnect: true,
26 max_reconnect_attempts: 5,
27 }
28 }
29}
30
31pub struct ClientConfigBuilder {
33 config: ClientConfig,
34}
35
36impl ClientConfigBuilder {
37 pub fn new() -> Self {
38 Self {
39 config: ClientConfig::default(),
40 }
41 }
42
43 pub fn user_name(mut self, name: impl Into<String>) -> Self {
44 self.config.user_name = name.into();
45 self
46 }
47
48 pub fn auto_reconnect(mut self, enabled: bool) -> Self {
49 self.config.auto_reconnect = enabled;
50 self
51 }
52
53 pub fn max_reconnect_attempts(mut self, attempts: u32) -> Self {
54 self.config.max_reconnect_attempts = attempts;
55 self
56 }
57
58 pub fn build(self) -> ClientConfig {
59 self.config
60 }
61}
62
63impl Default for ClientConfigBuilder {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69pub struct Client<T: NetworkTransport> {
93 peer_id: PeerId,
94 config: ClientConfig,
95 transport: Arc<T>,
96 sessions: Arc<RwLock<HashMap<String, Arc<Session<T>>>>>,
97}
98
99impl Client<MemoryTransport> {
100 pub fn new_with_memory_transport(config: ClientConfig) -> Self {
102 let peer_id = PeerId::new(format!("peer-{}", uuid_simple()));
103 let transport = Arc::new(MemoryTransport::new(peer_id.clone()));
104
105 Self {
106 peer_id,
107 config,
108 transport,
109 sessions: Arc::new(RwLock::new(HashMap::new())),
110 }
111 }
112}
113
114impl<T: NetworkTransport> Client<T> {
115 pub fn new(peer_id: PeerId, transport: Arc<T>, config: ClientConfig) -> Self {
117 Self {
118 peer_id,
119 config,
120 transport,
121 sessions: Arc::new(RwLock::new(HashMap::new())),
122 }
123 }
124
125 pub fn peer_id(&self) -> &PeerId {
127 &self.peer_id
128 }
129
130 pub fn user_name(&self) -> &str {
132 &self.config.user_name
133 }
134
135 pub fn transport(&self) -> &Arc<T> {
137 &self.transport
138 }
139
140 pub fn create_session(&self, session_id: impl Into<String>) -> Arc<Session<T>> {
142 let session_id = session_id.into();
143 let mut sessions = self.sessions.write();
144
145 if let Some(session) = sessions.get(&session_id) {
146 session.clone()
147 } else {
148 let session = Arc::new(Session::new(
149 session_id.clone(),
150 self.peer_id.clone(),
151 self.config.user_name.clone(),
152 self.transport.clone(),
153 ));
154 sessions.insert(session_id, session.clone());
155 session
156 }
157 }
158
159 pub fn get_session(&self, session_id: &str) -> Option<Arc<Session<T>>> {
161 self.sessions.read().get(session_id).cloned()
162 }
163
164 pub fn close_session(&self, session_id: &str) {
166 self.sessions.write().remove(session_id);
167 }
168
169 pub fn session_ids(&self) -> Vec<String> {
171 self.sessions.read().keys().cloned().collect()
172 }
173
174 pub async fn connect_peer(&self, peer_id: &PeerId) -> Result<(), SdkError> {
176 self.transport
177 .connect(peer_id)
178 .await
179 .map_err(|e| SdkError::ConnectionFailed(e.to_string()))
180 }
181
182 pub async fn disconnect_peer(&self, peer_id: &PeerId) -> Result<(), SdkError> {
184 self.transport
185 .disconnect(peer_id)
186 .await
187 .map_err(|e| SdkError::NetworkError(e.to_string()))
188 }
189
190 pub async fn connected_peers(&self) -> Vec<Peer> {
192 self.transport.connected_peers().await
193 }
194}
195
196fn uuid_simple() -> String {
198 use std::time::{SystemTime, UNIX_EPOCH};
199 let timestamp = SystemTime::now()
200 .duration_since(UNIX_EPOCH)
201 .unwrap()
202 .as_nanos();
203 format!("{:x}", timestamp)
204}
205
206pub mod quick {
208 use super::*;
209 use crate::network::create_network;
210
211 pub fn create_collaborative_clients(user_names: &[&str]) -> Vec<Client<MemoryTransport>> {
215 let network = create_network(user_names.len());
216
217 user_names
218 .iter()
219 .zip(network)
220 .map(|(name, transport)| {
221 let peer_id = transport.local_id().clone();
222 let config = ClientConfig {
223 user_name: name.to_string(),
224 ..Default::default()
225 };
226 Client::new(peer_id, Arc::new(transport), config)
227 })
228 .collect()
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_client_creation() {
238 let config = ClientConfig {
239 user_name: "Alice".to_string(),
240 ..Default::default()
241 };
242 let client = Client::new_with_memory_transport(config);
243
244 assert_eq!(client.user_name(), "Alice");
245 }
246
247 #[test]
248 fn test_session_management() {
249 let config = ClientConfig::default();
250 let client = Client::new_with_memory_transport(config);
251
252 let session1 = client.create_session("session-1");
253 let _session2 = client.create_session("session-2");
254
255 assert_eq!(client.session_ids().len(), 2);
256
257 let session1_again = client.create_session("session-1");
259 assert!(Arc::ptr_eq(&session1, &session1_again));
260
261 client.close_session("session-1");
263 assert_eq!(client.session_ids().len(), 1);
264 }
265
266 #[test]
267 fn test_config_builder() {
268 let config = ClientConfigBuilder::new()
269 .user_name("Bob")
270 .auto_reconnect(false)
271 .max_reconnect_attempts(3)
272 .build();
273
274 assert_eq!(config.user_name, "Bob");
275 assert!(!config.auto_reconnect);
276 assert_eq!(config.max_reconnect_attempts, 3);
277 }
278
279 #[test]
280 fn test_quick_collaborative_clients() {
281 let clients = quick::create_collaborative_clients(&["Alice", "Bob", "Charlie"]);
282
283 assert_eq!(clients.len(), 3);
284 assert_eq!(clients[0].user_name(), "Alice");
285 assert_eq!(clients[1].user_name(), "Bob");
286 assert_eq!(clients[2].user_name(), "Charlie");
287 }
288}