1use libp2p::PeerId;
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8use std::time::Duration;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ClientConfig {
13 pub enable_pubsub: bool,
15
16 pub enable_swarm: bool,
18
19 pub data_store_path: Option<PathBuf>,
21
22 pub listening_addrs: Vec<String>,
24
25 pub bootstrap_peers: Vec<PeerId>,
27
28 pub enable_mdns: bool,
30
31 pub enable_kad: bool,
33
34 pub network: NetworkConfig,
36
37 pub storage: StorageConfig,
39
40 pub pubsub: PubsubConfig,
42}
43
44impl Default for ClientConfig {
45 fn default() -> Self {
46 Self {
47 enable_pubsub: true,
48 enable_swarm: true,
49 data_store_path: Some(PathBuf::from("./ipfs_data")),
50 listening_addrs: vec![
51 "/ip4/0.0.0.0/tcp/0".to_string(),
52 "/ip6/::/tcp/0".to_string(),
53 ],
54 bootstrap_peers: vec![],
55 enable_mdns: true,
56 enable_kad: true,
57 network: NetworkConfig::default(),
58 storage: StorageConfig::default(),
59 pubsub: PubsubConfig::default(),
60 }
61 }
62}
63
64impl ClientConfig {
65 pub fn development() -> Self {
67 Self {
68 enable_pubsub: true, enable_swarm: true, data_store_path: Some("./tmp/ipfs_dev".into()),
71 listening_addrs: vec!["/ip4/127.0.0.1/tcp/0".to_string()],
72 bootstrap_peers: vec![],
73 enable_mdns: false,
74 enable_kad: false,
75 network: NetworkConfig::development(),
76 storage: StorageConfig::development(),
77 pubsub: PubsubConfig::development(),
78 }
79 }
80
81 pub fn production() -> Self {
83 Self {
84 enable_pubsub: true,
85 enable_swarm: true,
86 data_store_path: Some("/var/lib/ipfs".into()),
87 listening_addrs: vec![
88 "/ip4/0.0.0.0/tcp/4001".to_string(),
89 "/ip6/::/tcp/4001".to_string(),
90 "/ip4/0.0.0.0/tcp/8081/ws".to_string(),
91 ],
92 bootstrap_peers: vec![], enable_mdns: true,
94 enable_kad: true,
95 network: NetworkConfig::production(),
96 storage: StorageConfig::production(),
97 pubsub: PubsubConfig::production(),
98 }
99 }
100
101 pub fn testing() -> Self {
103 Self {
104 enable_pubsub: true,
105 enable_swarm: false,
106 data_store_path: None, listening_addrs: vec![],
108 bootstrap_peers: vec![],
109 enable_mdns: false,
110 enable_kad: false,
111 network: NetworkConfig::testing(),
112 storage: StorageConfig::testing(),
113 pubsub: PubsubConfig::testing(),
114 }
115 }
116
117 pub fn offline() -> Self {
119 Self {
120 enable_pubsub: false,
121 enable_swarm: false,
122 enable_mdns: false,
123 enable_kad: false,
124 ..Self::development()
125 }
126 }
127
128 pub fn add_bootstrap_peer(&mut self, peer: PeerId) {
130 if !self.bootstrap_peers.contains(&peer) {
131 self.bootstrap_peers.push(peer);
132 }
133 }
134
135 pub fn add_listening_addr(&mut self, addr: String) {
137 if !self.listening_addrs.contains(&addr) {
138 self.listening_addrs.push(addr);
139 }
140 }
141
142 pub fn with_data_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
144 self.data_store_path = Some(path.into());
145 self
146 }
147
148 pub fn validate(&self) -> Result<(), String> {
150 if self.enable_pubsub && !self.enable_swarm {
152 return Err("PubSub requer Swarm habilitado".to_string());
153 }
154
155 if self.enable_kad && !self.enable_swarm {
156 return Err("Kademlia DHT requer Swarm habilitado".to_string());
157 }
158
159 if self.enable_swarm && self.listening_addrs.is_empty() {
160 return Err("Swarm requer pelo menos um endereço de escuta".to_string());
161 }
162
163 for addr in &self.listening_addrs {
165 if addr.is_empty() {
166 return Err("Endereço de escuta não pode estar vazio".to_string());
167 }
168 }
169
170 Ok(())
171 }
172
173 pub fn hybrid() -> Self {
175 Self {
176 enable_pubsub: true, enable_swarm: true, data_store_path: Some("./hybrid_data".into()), listening_addrs: vec![
180 "/ip4/0.0.0.0/tcp/0".to_string(),
181 "/ip6/::/tcp/0".to_string(),
182 ],
183 bootstrap_peers: vec![],
184 enable_mdns: true, enable_kad: true, network: NetworkConfig::production(), storage: StorageConfig::production(), pubsub: PubsubConfig::production(), }
190 }
191
192 pub fn should_use_embedded(&self) -> bool {
194 self.data_store_path.is_some()
195 }
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct NetworkConfig {
201 pub connection_timeout: Duration,
203
204 pub max_connections: usize,
206
207 pub enable_relay: bool,
209
210 pub transport: TransportConfig,
212}
213
214impl Default for NetworkConfig {
215 fn default() -> Self {
216 Self {
217 connection_timeout: Duration::from_secs(30),
218 max_connections: 100,
219 enable_relay: false,
220 transport: TransportConfig::default(),
221 }
222 }
223}
224
225impl NetworkConfig {
226 pub fn development() -> Self {
227 Self {
228 connection_timeout: Duration::from_secs(10),
229 max_connections: 10,
230 enable_relay: false,
231 transport: TransportConfig::development(),
232 }
233 }
234
235 pub fn production() -> Self {
236 Self {
237 connection_timeout: Duration::from_secs(60),
238 max_connections: 1000,
239 enable_relay: true,
240 transport: TransportConfig::production(),
241 }
242 }
243
244 pub fn testing() -> Self {
245 Self {
246 connection_timeout: Duration::from_secs(5),
247 max_connections: 5,
248 enable_relay: false,
249 transport: TransportConfig::testing(),
250 }
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct TransportConfig {
257 pub enable_tcp: bool,
259
260 pub enable_quic: bool,
262
263 pub enable_websocket: bool,
265
266 pub security: SecurityConfig,
268}
269
270impl Default for TransportConfig {
271 fn default() -> Self {
272 Self {
273 enable_tcp: true,
274 enable_quic: false,
275 enable_websocket: false,
276 security: SecurityConfig::default(),
277 }
278 }
279}
280
281impl TransportConfig {
282 pub fn development() -> Self {
283 Self {
284 enable_tcp: true,
285 enable_quic: false,
286 enable_websocket: false,
287 security: SecurityConfig::development(),
288 }
289 }
290
291 pub fn production() -> Self {
292 Self {
293 enable_tcp: true,
294 enable_quic: true,
295 enable_websocket: true,
296 security: SecurityConfig::production(),
297 }
298 }
299
300 pub fn testing() -> Self {
301 Self {
302 enable_tcp: true,
303 enable_quic: false,
304 enable_websocket: false,
305 security: SecurityConfig::testing(),
306 }
307 }
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct SecurityConfig {
313 pub enable_noise: bool,
315
316 pub require_tls: bool,
318}
319
320impl Default for SecurityConfig {
321 fn default() -> Self {
322 Self {
323 enable_noise: true,
324 require_tls: false,
325 }
326 }
327}
328
329impl SecurityConfig {
330 pub fn development() -> Self {
331 Self {
332 enable_noise: false, require_tls: false,
334 }
335 }
336
337 pub fn production() -> Self {
338 Self {
339 enable_noise: true,
340 require_tls: true,
341 }
342 }
343
344 pub fn testing() -> Self {
345 Self {
346 enable_noise: false,
347 require_tls: false,
348 }
349 }
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct StorageConfig {
355 pub enable_memory_cache: bool,
357
358 pub max_cache_size: usize,
360
361 pub enable_compression: bool,
363
364 pub backend: StorageBackend,
366}
367
368impl Default for StorageConfig {
369 fn default() -> Self {
370 Self {
371 enable_memory_cache: true,
372 max_cache_size: 100 * 1024 * 1024, enable_compression: false,
374 backend: StorageBackend::Memory,
375 }
376 }
377}
378
379impl StorageConfig {
380 pub fn development() -> Self {
381 Self {
382 enable_memory_cache: true,
383 max_cache_size: 10 * 1024 * 1024, enable_compression: false,
385 backend: StorageBackend::Memory,
386 }
387 }
388
389 pub fn production() -> Self {
390 Self {
391 enable_memory_cache: true,
392 max_cache_size: 1024 * 1024 * 1024, enable_compression: true,
394 backend: StorageBackend::Sled,
395 }
396 }
397
398 pub fn testing() -> Self {
399 Self {
400 enable_memory_cache: false,
401 max_cache_size: 1024 * 1024, enable_compression: false,
403 backend: StorageBackend::Memory,
404 }
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub enum StorageBackend {
411 Memory,
413 Sled,
415 FileSystem,
417}
418
419#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct PubsubConfig {
422 pub enable_message_validation: bool,
424
425 pub max_message_size: usize,
427
428 pub message_buffer_size: usize,
430
431 pub operation_timeout: Duration,
433}
434
435impl Default for PubsubConfig {
436 fn default() -> Self {
437 Self {
438 enable_message_validation: true,
439 max_message_size: 1024 * 1024, message_buffer_size: 1000,
441 operation_timeout: Duration::from_secs(30),
442 }
443 }
444}
445
446impl PubsubConfig {
447 pub fn development() -> Self {
448 Self {
449 enable_message_validation: false,
450 max_message_size: 64 * 1024, message_buffer_size: 100,
452 operation_timeout: Duration::from_secs(10),
453 }
454 }
455
456 pub fn production() -> Self {
457 Self {
458 enable_message_validation: true,
459 max_message_size: 10 * 1024 * 1024, message_buffer_size: 10000,
461 operation_timeout: Duration::from_secs(60),
462 }
463 }
464
465 pub fn testing() -> Self {
466 Self {
467 enable_message_validation: false,
468 max_message_size: 1024, message_buffer_size: 10,
470 operation_timeout: Duration::from_secs(5),
471 }
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 #[test]
480 fn test_default_config() {
481 let config = ClientConfig::default();
482 assert!(config.enable_pubsub);
483 assert!(config.enable_swarm);
484 assert!(config.validate().is_ok());
485 }
486
487 #[test]
488 fn test_development_config() {
489 let config = ClientConfig::development();
490 assert!(config.enable_pubsub);
491 assert!(config.enable_swarm);
492 assert_eq!(config.listening_addrs.len(), 1);
493 }
494
495 #[test]
496 fn test_production_config() {
497 let config = ClientConfig::production();
498 assert!(config.enable_pubsub);
499 assert!(config.enable_swarm);
500 assert!(config.listening_addrs.len() >= 2);
501 }
502
503 #[test]
504 fn test_testing_config() {
505 let config = ClientConfig::testing();
506 assert!(config.enable_pubsub);
507 assert!(!config.enable_swarm);
508 assert!(config.listening_addrs.is_empty());
509 }
510
511 #[test]
512 fn test_offline_config() {
513 let config = ClientConfig::offline();
514 assert!(!config.enable_pubsub);
515 assert!(!config.enable_swarm);
516 assert!(!config.enable_mdns);
517 assert!(!config.enable_kad);
518 }
519
520 #[test]
521 fn test_config_validation() {
522 let mut config = ClientConfig::default();
523
524 assert!(config.validate().is_ok());
526
527 config.enable_pubsub = true;
529 config.enable_swarm = false;
530 assert!(config.validate().is_err());
531
532 config.enable_swarm = true;
534 config.listening_addrs.clear();
535 assert!(config.validate().is_err());
536 }
537
538 #[test]
539 fn test_add_bootstrap_peer() {
540 let mut config = ClientConfig::default();
541 let peer = PeerId::random();
542
543 config.add_bootstrap_peer(peer);
544 assert!(config.bootstrap_peers.contains(&peer));
545
546 config.add_bootstrap_peer(peer);
548 assert_eq!(config.bootstrap_peers.len(), 1);
549 }
550
551 #[test]
552 fn test_with_data_path() {
553 let config = ClientConfig::default().with_data_path("/custom/path");
554
555 assert_eq!(config.data_store_path, Some(PathBuf::from("/custom/path")));
556 }
557
558 #[test]
559 fn test_hybrid_configuration() {
560 let config = ClientConfig::hybrid();
561
562 assert!(config.should_use_embedded());
563 assert!(config.enable_pubsub);
564 assert!(config.enable_swarm);
565 assert!(config.enable_kad);
566 }
567
568 #[test]
569 fn test_backend_detection() {
570 let embedded_config = ClientConfig::development();
572 assert!(embedded_config.should_use_embedded());
573
574 let hybrid_config = ClientConfig::hybrid();
576 assert!(hybrid_config.should_use_embedded());
577 }
578}