Skip to main content

peat_mesh/
config.rs

1//! Configuration types for the PeatMesh facade.
2
3use crate::topology::TopologyConfig;
4use crate::transport::TransportManagerConfig;
5use std::path::PathBuf;
6use std::time::Duration;
7
8/// Configuration for the Iroh networking layer.
9#[derive(Debug, Clone)]
10pub struct IrohConfig {
11    /// Explicit bind address for the Iroh endpoint.
12    /// When `None`, Iroh picks an available port automatically.
13    pub bind_addr: Option<std::net::SocketAddr>,
14    /// Relay server URLs for NAT traversal.
15    /// Empty means use Iroh's default relay infrastructure.
16    pub relay_urls: Vec<String>,
17    /// Deterministic Iroh identity seed (32 bytes).
18    /// When `Some`, used as `SecretKey::from_bytes()` for the Iroh endpoint,
19    /// giving the node a stable, reproducible identity.
20    pub secret_key: Option<[u8; 32]>,
21    /// Timeout for binding the QUIC endpoint (default: 10s).
22    pub bind_timeout: Duration,
23    /// Timeout for graceful shutdown (default: 5s).
24    pub shutdown_timeout: Duration,
25    /// Timeout for blob download operations from remote peers (default: 30s).
26    pub download_timeout: Duration,
27}
28
29impl Default for IrohConfig {
30    fn default() -> Self {
31        Self {
32            bind_addr: None,
33            relay_urls: Vec::new(),
34            secret_key: None,
35            bind_timeout: Duration::from_secs(10),
36            shutdown_timeout: Duration::from_secs(5),
37            download_timeout: Duration::from_secs(30),
38        }
39    }
40}
41
42/// Top-level configuration for PeatMesh.
43///
44/// Composes topology, discovery, and security settings into a single
45/// configuration struct that drives the [`crate::mesh::PeatMesh`] facade.
46#[derive(Debug, Clone, Default)]
47pub struct MeshConfig {
48    /// Node identifier. Auto-generated (UUID v4) if `None`.
49    pub node_id: Option<String>,
50    /// Optional path for persistent storage.
51    pub storage_path: Option<PathBuf>,
52    /// Topology formation configuration.
53    pub topology: TopologyConfig,
54    /// Peer discovery configuration.
55    pub discovery: MeshDiscoveryConfig,
56    /// Security configuration.
57    pub security: SecurityConfig,
58    /// Transport manager configuration for multi-transport selection.
59    pub transport_manager: Option<TransportManagerConfig>,
60    /// Iroh networking configuration.
61    pub iroh: IrohConfig,
62}
63
64/// Discovery settings for mesh peer discovery.
65#[derive(Debug, Clone)]
66pub struct MeshDiscoveryConfig {
67    /// Enable mDNS-based peer discovery.
68    pub mdns_enabled: bool,
69    /// Service name advertised during discovery.
70    pub service_name: String,
71    /// Discovery broadcast interval.
72    pub interval: Duration,
73}
74
75impl Default for MeshDiscoveryConfig {
76    fn default() -> Self {
77        Self {
78            mdns_enabled: true,
79            service_name: "peat-mesh".to_string(),
80            interval: Duration::from_secs(30),
81        }
82    }
83}
84
85/// Security settings for mesh communications.
86#[derive(Debug, Clone)]
87pub struct SecurityConfig {
88    /// Enable encryption for mesh communications.
89    pub encryption_enabled: bool,
90    /// Require cryptographic peer identity verification.
91    pub require_peer_verification: bool,
92}
93
94impl Default for SecurityConfig {
95    fn default() -> Self {
96        Self {
97            encryption_enabled: true,
98            require_peer_verification: false,
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    // ── MeshConfig defaults ──────────────────────────────────────
108
109    #[test]
110    fn test_mesh_config_default_node_id_is_none() {
111        let cfg = MeshConfig::default();
112        assert!(cfg.node_id.is_none());
113    }
114
115    #[test]
116    fn test_mesh_config_default_storage_path_is_none() {
117        let cfg = MeshConfig::default();
118        assert!(cfg.storage_path.is_none());
119    }
120
121    #[test]
122    fn test_mesh_config_default_topology() {
123        let cfg = MeshConfig::default();
124        // TopologyConfig default reevaluation_interval is 30s
125        assert_eq!(
126            cfg.topology.reevaluation_interval,
127            Some(Duration::from_secs(30))
128        );
129    }
130
131    #[test]
132    fn test_mesh_config_default_discovery() {
133        let cfg = MeshConfig::default();
134        assert!(cfg.discovery.mdns_enabled);
135        assert_eq!(cfg.discovery.service_name, "peat-mesh");
136    }
137
138    #[test]
139    fn test_mesh_config_default_security() {
140        let cfg = MeshConfig::default();
141        assert!(cfg.security.encryption_enabled);
142        assert!(!cfg.security.require_peer_verification);
143    }
144
145    #[test]
146    fn test_mesh_config_custom_values() {
147        let cfg = MeshConfig {
148            node_id: Some("custom-node".to_string()),
149            storage_path: Some(PathBuf::from("/tmp/mesh")),
150            discovery: MeshDiscoveryConfig {
151                mdns_enabled: false,
152                service_name: "my-mesh".to_string(),
153                interval: Duration::from_secs(10),
154            },
155            security: SecurityConfig {
156                encryption_enabled: false,
157                require_peer_verification: true,
158            },
159            ..Default::default()
160        };
161        assert_eq!(cfg.node_id.as_deref(), Some("custom-node"));
162        assert_eq!(
163            cfg.storage_path.as_deref(),
164            Some(std::path::Path::new("/tmp/mesh"))
165        );
166        assert!(!cfg.discovery.mdns_enabled);
167        assert_eq!(cfg.discovery.service_name, "my-mesh");
168        assert_eq!(cfg.discovery.interval, Duration::from_secs(10));
169        assert!(!cfg.security.encryption_enabled);
170        assert!(cfg.security.require_peer_verification);
171    }
172
173    #[test]
174    fn test_mesh_config_clone() {
175        let cfg = MeshConfig {
176            node_id: Some("cloned".to_string()),
177            ..Default::default()
178        };
179        let cloned = cfg.clone();
180        assert_eq!(cloned.node_id, cfg.node_id);
181    }
182
183    #[test]
184    fn test_mesh_config_debug() {
185        let cfg = MeshConfig::default();
186        let debug = format!("{:?}", cfg);
187        assert!(debug.contains("MeshConfig"));
188    }
189
190    // ── MeshDiscoveryConfig defaults ─────────────────────────────
191
192    #[test]
193    fn test_discovery_config_default_mdns_enabled() {
194        let cfg = MeshDiscoveryConfig::default();
195        assert!(cfg.mdns_enabled);
196    }
197
198    #[test]
199    fn test_discovery_config_default_service_name() {
200        let cfg = MeshDiscoveryConfig::default();
201        assert_eq!(cfg.service_name, "peat-mesh");
202    }
203
204    #[test]
205    fn test_discovery_config_default_interval() {
206        let cfg = MeshDiscoveryConfig::default();
207        assert_eq!(cfg.interval, Duration::from_secs(30));
208    }
209
210    #[test]
211    fn test_discovery_config_custom() {
212        let cfg = MeshDiscoveryConfig {
213            mdns_enabled: false,
214            service_name: "custom".to_string(),
215            interval: Duration::from_secs(5),
216        };
217        assert!(!cfg.mdns_enabled);
218        assert_eq!(cfg.service_name, "custom");
219        assert_eq!(cfg.interval, Duration::from_secs(5));
220    }
221
222    #[test]
223    fn test_discovery_config_clone() {
224        let cfg = MeshDiscoveryConfig::default();
225        let cloned = cfg.clone();
226        assert_eq!(cloned.service_name, cfg.service_name);
227    }
228
229    #[test]
230    fn test_discovery_config_debug() {
231        let cfg = MeshDiscoveryConfig::default();
232        let debug = format!("{:?}", cfg);
233        assert!(debug.contains("MeshDiscoveryConfig"));
234    }
235
236    // ── IrohConfig defaults ───────────────────────────────────────
237
238    #[test]
239    fn test_iroh_config_default() {
240        let cfg = IrohConfig::default();
241        assert!(cfg.bind_addr.is_none());
242        assert!(cfg.relay_urls.is_empty());
243        assert_eq!(cfg.bind_timeout, Duration::from_secs(10));
244        assert_eq!(cfg.shutdown_timeout, Duration::from_secs(5));
245        assert_eq!(cfg.download_timeout, Duration::from_secs(30));
246    }
247
248    #[test]
249    fn test_iroh_config_custom_bind_addr() {
250        let cfg = IrohConfig {
251            bind_addr: Some("0.0.0.0:4433".parse().unwrap()),
252            relay_urls: vec!["https://relay.example.com".to_string()],
253            ..Default::default()
254        };
255        assert_eq!(cfg.bind_addr.unwrap().port(), 4433);
256        assert_eq!(cfg.relay_urls.len(), 1);
257    }
258
259    #[test]
260    fn test_iroh_config_secret_key() {
261        let key = [42u8; 32];
262        let cfg = IrohConfig {
263            secret_key: Some(key),
264            ..Default::default()
265        };
266        assert_eq!(cfg.secret_key.unwrap(), [42u8; 32]);
267    }
268
269    #[test]
270    fn test_mesh_config_default_iroh() {
271        let cfg = MeshConfig::default();
272        assert!(cfg.iroh.bind_addr.is_none());
273        assert!(cfg.iroh.relay_urls.is_empty());
274    }
275
276    // ── SecurityConfig defaults ──────────────────────────────────
277
278    #[test]
279    fn test_security_config_default_encryption_enabled() {
280        let cfg = SecurityConfig::default();
281        assert!(cfg.encryption_enabled);
282    }
283
284    #[test]
285    fn test_security_config_default_peer_verification_disabled() {
286        let cfg = SecurityConfig::default();
287        assert!(!cfg.require_peer_verification);
288    }
289
290    #[test]
291    fn test_security_config_custom() {
292        let cfg = SecurityConfig {
293            encryption_enabled: false,
294            require_peer_verification: true,
295        };
296        assert!(!cfg.encryption_enabled);
297        assert!(cfg.require_peer_verification);
298    }
299
300    #[test]
301    fn test_security_config_clone() {
302        let cfg = SecurityConfig {
303            encryption_enabled: true,
304            require_peer_verification: true,
305        };
306        let cloned = cfg.clone();
307        assert_eq!(cloned.encryption_enabled, cfg.encryption_enabled);
308        assert_eq!(
309            cloned.require_peer_verification,
310            cfg.require_peer_verification
311        );
312    }
313
314    #[test]
315    fn test_security_config_debug() {
316        let cfg = SecurityConfig::default();
317        let debug = format!("{:?}", cfg);
318        assert!(debug.contains("SecurityConfig"));
319    }
320}