Skip to main content

hive_mesh/
config.rs

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