Skip to main content

ip_discovery/
config.rs

1//! Configuration for IP detection.
2//!
3//! Use [`Config::builder()`] to create a customized configuration,
4//! or [`Config::default()`] for sensible defaults (all protocols, first-success strategy).
5
6use crate::provider::BoxedProvider;
7use crate::types::{BuiltinProvider, IpVersion, Protocol};
8use std::time::Duration;
9
10/// Strategy for resolving the public IP across multiple providers.
11#[derive(Debug, Clone, Copy, Default)]
12pub enum Strategy {
13    /// Try providers sequentially, return the first success.
14    #[default]
15    First,
16    /// Race all providers concurrently, return the fastest success.
17    Race,
18    /// Query all providers, require multiple to agree on the same IP.
19    Consensus {
20        /// Minimum number of providers that must return the same IP.
21        /// Automatically clamped to at least 2.
22        min_agree: usize,
23    },
24}
25
26/// Configuration for IP detection
27pub struct Config {
28    /// List of providers to use
29    pub(crate) providers: Vec<BoxedProvider>,
30    /// Timeout for each provider
31    pub(crate) timeout: Duration,
32    /// IP version preference
33    pub(crate) version: IpVersion,
34    /// Resolution strategy
35    pub(crate) strategy: Strategy,
36}
37
38impl Default for Config {
39    fn default() -> Self {
40        Self::builder().build()
41    }
42}
43
44impl Config {
45    /// Create a new configuration builder
46    pub fn builder() -> ConfigBuilder {
47        ConfigBuilder::new()
48    }
49}
50
51/// Builder for [`Config`].
52///
53/// Created via [`Config::builder()`]. Call methods to customize, then
54/// [`.build()`](ConfigBuilder::build) to produce the final [`Config`].
55pub struct ConfigBuilder {
56    custom_providers: Vec<BoxedProvider>,
57    timeout: Duration,
58    version: IpVersion,
59    strategy: Strategy,
60    provider_filter: Option<ProviderFilter>,
61}
62
63/// Filter to select which providers to include
64enum ProviderFilter {
65    /// Only providers of specified protocols
66    Protocols(Vec<Protocol>),
67    /// Specific built-in providers
68    Select(Vec<BuiltinProvider>),
69}
70
71impl ConfigBuilder {
72    /// Create a new builder with default settings
73    pub fn new() -> Self {
74        Self {
75            custom_providers: Vec::new(),
76            timeout: Duration::from_secs(10),
77            version: IpVersion::Any,
78            strategy: Strategy::First,
79            provider_filter: None,
80        }
81    }
82
83    /// Filter providers by protocol (e.g., DNS, HTTP, STUN)
84    ///
85    /// # Example
86    /// ```rust,no_run
87    /// use ip_discovery::{Config, Protocol};
88    ///
89    /// let config = Config::builder()
90    ///     .protocols(&[Protocol::Dns, Protocol::Stun])
91    ///     .build();
92    /// ```
93    pub fn protocols(mut self, protocols: &[Protocol]) -> Self {
94        self.provider_filter = Some(ProviderFilter::Protocols(protocols.to_vec()));
95        self
96    }
97
98    /// Select specific built-in providers
99    ///
100    /// # Example
101    /// ```rust,no_run
102    /// use ip_discovery::{Config, BuiltinProvider};
103    ///
104    /// let config = Config::builder()
105    ///     .providers(&[
106    ///         BuiltinProvider::CloudflareDns,
107    ///         BuiltinProvider::GoogleStun,
108    ///     ])
109    ///     .build();
110    /// ```
111    pub fn providers(mut self, providers: &[BuiltinProvider]) -> Self {
112        self.provider_filter = Some(ProviderFilter::Select(providers.to_vec()));
113        self
114    }
115
116    /// Add a custom provider (advanced usage)
117    ///
118    /// Custom providers are added alongside any filter-selected providers.
119    pub fn add_provider(mut self, provider: BoxedProvider) -> Self {
120        self.custom_providers.push(provider);
121        self
122    }
123
124    /// Set timeout for each provider
125    pub fn timeout(mut self, timeout: Duration) -> Self {
126        self.timeout = timeout;
127        self
128    }
129
130    /// Set IP version preference
131    pub fn version(mut self, version: IpVersion) -> Self {
132        self.version = version;
133        self
134    }
135
136    /// Set resolution strategy
137    pub fn strategy(mut self, strategy: Strategy) -> Self {
138        self.strategy = strategy;
139        self
140    }
141
142    /// Build the configuration
143    pub fn build(mut self) -> Config {
144        let mut providers: Vec<BoxedProvider> = match self.provider_filter.take() {
145            Some(ProviderFilter::Protocols(protocols)) => BuiltinProvider::ALL
146                .iter()
147                .filter(|p| protocols.contains(&p.protocol()))
148                .map(|p| p.to_boxed())
149                .collect(),
150            Some(ProviderFilter::Select(selected)) => {
151                selected.into_iter().map(|p| p.to_boxed()).collect()
152            }
153            None if self.custom_providers.is_empty() => {
154                BuiltinProvider::ALL.iter().map(|p| p.to_boxed()).collect()
155            }
156            None => Vec::new(),
157        };
158
159        // Append any custom providers added via add_provider()
160        providers.append(&mut self.custom_providers);
161
162        Config {
163            providers,
164            timeout: self.timeout,
165            version: self.version,
166            strategy: self.strategy,
167        }
168    }
169}
170
171impl Default for ConfigBuilder {
172    fn default() -> Self {
173        Self::new()
174    }
175}