auto_discovery/
config.rs

1//! Configuration types for service discovery
2
3use crate::types::{ProtocolType, ServiceType, DiscoveryFilter};
4use crate::error::Result;
5use serde::{Deserialize, Serialize};
6use std::{collections::HashSet, time::Duration};
7
8/// Configuration for the service discovery system
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DiscoveryConfig {
11    /// Service types to discover
12    service_types: Vec<ServiceType>,
13    /// Operation timeout
14    timeout: Option<Duration>,
15    /// Whether to verify discovered services
16    verify_services: bool,
17    /// Network interfaces to use
18    interfaces: Option<HashSet<String>>,
19    /// Maximum number of services to track
20    max_services: usize,
21    /// Maximum number of retries
22    max_retries: u32,
23    /// Cache duration
24    cache_duration: Duration,
25    /// Rate limit for discovery
26    rate_limit: Option<Duration>,
27    /// Whether metrics are enabled
28    metrics_enabled: bool,
29    /// Enabled protocols
30    enabled_protocols: HashSet<ProtocolType>,
31    /// Whether to allow cross-protocol discovery
32    allow_cross_protocol: bool,
33    /// Whether to enable IPv4 support
34    enable_ipv4: bool,
35    /// Whether to enable IPv6 support
36    enable_ipv6: bool,
37    /// Discovery filter
38    filter: Option<DiscoveryFilter>,
39}
40
41impl Default for DiscoveryConfig {
42    fn default() -> Self {
43        Self {
44            service_types: Vec::new(),
45            timeout: Some(Duration::from_secs(30)),
46            verify_services: false,
47            interfaces: None,
48            max_services: 1000,
49            max_retries: 3,
50            cache_duration: Duration::from_secs(300),
51            rate_limit: Some(Duration::from_secs(1)),
52            metrics_enabled: false,
53            enabled_protocols: [ProtocolType::Mdns].into_iter().collect(),
54            allow_cross_protocol: false,
55            enable_ipv4: true,
56            enable_ipv6: false,
57            filter: None,
58        }
59    }
60}
61
62impl DiscoveryConfig {
63    /// Create a new configuration with default values
64    pub fn new() -> Self {
65        Self::default()
66    }
67
68    /// Set timeout for operations
69    pub fn with_timeout(mut self, timeout: Duration) -> Self {
70        self.timeout = Some(timeout);
71        self
72    }
73
74    /// Get operation timeout
75    pub fn timeout(&self) -> Option<Duration> {
76        self.timeout
77    }
78
79    /// Set service types to discover
80    pub fn with_service_type(mut self, service_type: ServiceType) -> Self {
81        self.service_types.push(service_type);
82        self
83    }
84
85    /// Get service types
86    pub fn service_types(&self) -> &[ServiceType] {
87        &self.service_types
88    }
89
90    /// Set service verification flag
91    pub fn with_verify_services(mut self, verify: bool) -> Self {
92        self.verify_services = verify;
93        self
94    }
95
96    /// Check if service verification is enabled
97    pub fn verify_services(&self) -> bool {
98        self.verify_services
99    }
100
101    /// Set network interfaces
102    pub fn with_interfaces(mut self, interfaces: HashSet<String>) -> Self {
103        self.interfaces = Some(interfaces);
104        self
105    }
106
107    /// Get network interfaces
108    pub fn interfaces(&self) -> Option<&HashSet<String>> {
109        self.interfaces.as_ref()
110    }
111
112    /// Set maximum number of services
113    pub fn with_max_services(mut self, max: usize) -> Self {
114        self.max_services = max;
115        self
116    }
117
118    /// Get maximum number of services
119    pub fn max_services(&self) -> usize {
120        self.max_services
121    }
122
123    /// Set maximum number of retries
124    pub fn with_max_retries(mut self, retries: u32) -> Self {
125        self.max_retries = retries;
126        self
127    }
128
129    /// Get maximum number of retries
130    pub fn max_retries(&self) -> u32 {
131        self.max_retries
132    }
133
134    /// Enable IPv4 support
135    pub fn with_ipv4(mut self, enable: bool) -> Self {
136        self.enable_ipv4 = enable;
137        self
138    }
139
140    /// Get IPv4 support status
141    pub fn enable_ipv4(&self) -> bool {
142        self.enable_ipv4
143    }
144
145    /// Enable IPv6 support
146    pub fn with_ipv6(mut self, enable: bool) -> Self {
147        self.enable_ipv6 = enable;
148        self
149    }
150
151    /// Get IPv6 support status
152    pub fn enable_ipv6(&self) -> bool {
153        self.enable_ipv6
154    }
155
156    /// Enable a protocol
157    pub fn with_protocol(mut self, protocol: ProtocolType) -> Self {
158        self.enabled_protocols.insert(protocol);
159        self
160    }
161
162    /// Check if a protocol is enabled
163    pub fn is_protocol_enabled(&self, protocol: ProtocolType) -> bool {
164        self.enabled_protocols.contains(&protocol)
165    }
166
167    /// Enable a protocol
168    pub fn enable_protocol(&mut self, protocol: ProtocolType) {
169        self.enabled_protocols.insert(protocol);
170    }
171
172    /// Disable a protocol
173    pub fn disable_protocol(&mut self, protocol: ProtocolType) {
174        self.enabled_protocols.remove(&protocol);
175    }
176
177    /// Set enabled protocols
178    pub fn with_protocols(mut self, protocols: HashSet<ProtocolType>) -> Self {
179        self.enabled_protocols = protocols;
180        self
181    }
182
183    /// Get enabled protocols
184    pub fn protocols(&self) -> &HashSet<ProtocolType> {
185        &self.enabled_protocols
186    }
187
188    /// Enable cross-protocol discovery
189    pub fn with_cross_protocol(mut self, enable: bool) -> Self {
190        self.allow_cross_protocol = enable;
191        self
192    }
193
194    /// Get cross-protocol discovery status
195    pub fn allow_cross_protocol(&self) -> bool {
196        self.allow_cross_protocol
197    }
198
199    /// Enable metrics
200    pub fn with_metrics(mut self, enable: bool) -> Self {
201        self.metrics_enabled = enable;
202        self
203    }
204
205    /// Get metrics status
206    pub fn metrics_enabled(&self) -> bool {
207        self.metrics_enabled
208    }
209
210    /// Set rate limit
211    pub fn with_rate_limit(mut self, limit: Duration) -> Self {
212        self.rate_limit = Some(limit);
213        self
214    }
215
216    /// Get rate limit
217    pub fn rate_limit(&self) -> Option<Duration> {
218        self.rate_limit
219    }
220
221    /// Set cache duration
222    pub fn with_cache_duration(mut self, duration: Duration) -> Self {
223        self.cache_duration = duration;
224        self
225    }
226
227    /// Get cache duration
228    pub fn cache_duration(&self) -> Duration {
229        self.cache_duration
230    }
231
232    /// Get protocol timeout
233    pub fn protocol_timeout(&self) -> Duration {
234        self.timeout.unwrap_or(Duration::from_secs(30))
235    }
236
237    /// Check if a protocol is enabled (alias for is_protocol_enabled)
238    pub fn has_protocol(&self, protocol: ProtocolType) -> bool {
239        self.enabled_protocols.contains(&protocol)
240    }
241
242    /// Get the discovery filter
243    pub fn filter(&self) -> Option<&DiscoveryFilter> {
244        self.filter.as_ref()
245    }
246
247    /// Set discovery filter
248    pub fn with_filter(mut self, filter: DiscoveryFilter) -> Self {
249        self.filter = Some(filter);
250        self
251    }
252
253    /// Validate configuration
254    pub fn validate(&self) -> Result<()> {
255        if self.timeout.is_some_and(|t| t.as_secs() == 0) {
256            return Err(crate::error::DiscoveryError::configuration(
257                "Timeout must be greater than 0",
258            ));
259        }
260
261        if !self.enable_ipv4 && !self.enable_ipv6 {
262            return Err(crate::error::DiscoveryError::configuration(
263                "Either IPv4 or IPv6 must be enabled",
264            ));
265        }
266
267        if self.enabled_protocols.is_empty() {
268            return Err(crate::error::DiscoveryError::configuration(
269                "At least one protocol must be enabled",
270            ));
271        }
272
273        Ok(())
274    }
275}
276
277/// Configuration for service registration
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct RegistrationConfig {
280    /// Time-to-live for the service record
281    pub ttl: Duration,
282    /// Whether to auto-refresh the service registration
283    pub auto_refresh: bool,
284    /// Refresh interval (only used if auto_refresh is true)
285    pub refresh_interval: Duration,
286    /// Network interfaces to register on
287    pub interfaces: Vec<String>,
288    /// Protocols to use for registration
289    pub protocols: HashSet<ProtocolType>,
290    /// Whether to enable IPv6 registration
291    pub enable_ipv6: bool,
292    /// Whether to enable IPv4 registration
293    pub enable_ipv4: bool,
294    /// Priority for the service (used in some protocols)
295    pub priority: u16,
296    /// Weight for the service (used in some protocols)
297    pub weight: u16,
298}
299
300impl Default for RegistrationConfig {
301    fn default() -> Self {
302        let mut protocols = HashSet::new();
303        protocols.insert(ProtocolType::Mdns);
304
305        Self {
306            ttl: Duration::from_secs(120),
307            auto_refresh: true,
308            refresh_interval: Duration::from_secs(60),
309            interfaces: Vec::new(),
310            protocols,
311            enable_ipv6: true,
312            enable_ipv4: true,
313            priority: 0,
314            weight: 0,
315        }
316    }
317}
318
319impl RegistrationConfig {
320    /// Create a new registration configuration with defaults
321    pub fn new() -> Self {
322        Self::default()
323    }
324
325    /// Set the TTL for service records
326    pub fn ttl(mut self, ttl: Duration) -> Self {
327        self.ttl = ttl;
328        self
329    }
330
331    /// Enable or disable auto-refresh
332    pub fn auto_refresh(mut self, auto_refresh: bool) -> Self {
333        self.auto_refresh = auto_refresh;
334        self
335    }
336
337    /// Set the refresh interval
338    pub fn refresh_interval(mut self, interval: Duration) -> Self {
339        self.refresh_interval = interval;
340        self
341    }
342
343    /// Set network interfaces for registration
344    pub fn interfaces<I, S>(mut self, interfaces: I) -> Self
345    where
346        I: IntoIterator<Item = S>,
347        S: Into<String>,
348    {
349        self.interfaces = interfaces.into_iter().map(|s| s.into()).collect();
350        self
351    }
352
353    /// Set registration protocols
354    pub fn protocols<I>(mut self, protocols: I) -> Self
355    where
356        I: IntoIterator<Item = ProtocolType>,
357    {
358        self.protocols = protocols.into_iter().collect();
359        self
360    }
361
362    /// Set service priority
363    pub fn priority(mut self, priority: u16) -> Self {
364        self.priority = priority;
365        self
366    }
367
368    /// Set service weight
369    pub fn weight(mut self, weight: u16) -> Self {
370        self.weight = weight;
371        self
372    }
373
374    /// Validate the configuration
375    pub fn validate(&self) -> crate::Result<()> {
376        if self.ttl.is_zero() {
377            return Err(crate::error::DiscoveryError::configuration(
378                "TTL cannot be zero",
379            ));
380        }
381
382        if self.auto_refresh && self.refresh_interval.is_zero() {
383            return Err(crate::error::DiscoveryError::configuration(
384                "Refresh interval cannot be zero when auto-refresh is enabled",
385            ));
386        }
387
388        if self.auto_refresh && self.refresh_interval >= self.ttl {
389            return Err(crate::error::DiscoveryError::configuration(
390                "Refresh interval must be less than TTL",
391            ));
392        }
393
394        if self.protocols.is_empty() {
395            return Err(crate::error::DiscoveryError::configuration(
396                "At least one protocol must be enabled",
397            ));
398        }
399
400        if !self.enable_ipv4 && !self.enable_ipv6 {
401            return Err(crate::error::DiscoveryError::configuration(
402                "At least one IP version must be enabled",
403            ));
404        }
405
406        Ok(())
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use super::*;
413    use std::time::Duration;
414
415    #[test]
416    fn test_config_defaults() {
417        let config = DiscoveryConfig::new();
418        assert_eq!(config.service_types().len(), 0);
419        assert!(config.timeout().is_some());
420        assert!(!config.verify_services());
421        assert!(config.is_protocol_enabled(ProtocolType::Mdns));
422    }
423
424    #[test]
425    fn test_config_builder() -> Result<()> {
426        let config = DiscoveryConfig::new()
427            .with_service_type(ServiceType::new("_http._tcp")?);
428        assert_eq!(config.service_types().len(), 1);
429        Ok(())
430    }
431
432    #[test]
433    fn test_config_validation() -> Result<()> {
434        let config = DiscoveryConfig::new()
435            .with_service_type(ServiceType::new("_http._tcp")?)
436            .with_timeout(Duration::from_secs(10))
437            .with_verify_services(true);
438
439        assert!(config.validate().is_ok());
440
441        // Test invalid timeout
442        let invalid_config = DiscoveryConfig::new()
443            .with_timeout(Duration::ZERO);
444        assert!(invalid_config.validate().is_err());
445
446        // Test invalid network config
447        let invalid_config = DiscoveryConfig::new()
448            .with_ipv4(false)
449            .with_ipv6(false);
450        assert!(invalid_config.validate().is_err());
451
452        Ok(())
453    }
454}