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}