Skip to main content

ip_discovery/
provider.rs

1//! Provider traits and boxed type aliases.
2//!
3//! All IP detection backends (DNS, HTTP, STUN) implement [`BlockingProvider`]
4//! for synchronous execution, and optionally [`Provider`] for async execution.
5//! Custom providers can also be created by implementing these traits.
6
7use crate::error::ProviderError;
8use crate::types::{IpVersion, Protocol};
9use std::net::IpAddr;
10use std::time::Duration;
11
12#[cfg(feature = "tokio")]
13use std::future::Future;
14#[cfg(feature = "tokio")]
15use std::pin::Pin;
16
17/// Trait for synchronous IP detection providers
18pub trait BlockingProvider: Send + Sync {
19    /// Provider name for identification
20    fn name(&self) -> &str;
21
22    /// Protocol used by this provider
23    fn protocol(&self) -> Protocol;
24
25    /// Whether this provider supports IPv4
26    fn supports_v4(&self) -> bool {
27        true
28    }
29
30    /// Whether this provider supports IPv6
31    fn supports_v6(&self) -> bool {
32        false
33    }
34
35    /// Check if provider supports the given IP version
36    fn supports_version(&self, version: IpVersion) -> bool {
37        match version {
38            IpVersion::V4 => self.supports_v4(),
39            IpVersion::V6 => self.supports_v6(),
40            IpVersion::Any => self.supports_v4() || self.supports_v6(),
41        }
42    }
43
44    /// Get the public IP address synchronously with the given timeout.
45    ///
46    /// Implementations should honor `timeout` for their own I/O. The blocking
47    /// resolver also enforces the caller-visible deadline, but Rust cannot
48    /// forcibly cancel an OS thread that is already executing provider code;
49    /// work from a non-cooperative custom provider may continue in the
50    /// background after the resolver returns a timeout.
51    fn get_ip(&self, version: IpVersion, timeout: Duration) -> Result<IpAddr, ProviderError>;
52
53    /// Clone this provider into a boxed trait object
54    fn clone_box(&self) -> BoxedBlockingProvider;
55}
56
57/// Type-erased synchronous provider, used internally to store heterogeneous providers.
58pub type BoxedBlockingProvider = Box<dyn BlockingProvider>;
59
60impl Clone for BoxedBlockingProvider {
61    fn clone(&self) -> Self {
62        self.clone_box()
63    }
64}
65
66/// Trait for asynchronous IP detection providers
67#[cfg(feature = "tokio")]
68pub trait Provider: Send + Sync {
69    /// Provider name for identification
70    fn name(&self) -> &str;
71
72    /// Protocol used by this provider
73    fn protocol(&self) -> Protocol;
74
75    /// Whether this provider supports IPv4
76    fn supports_v4(&self) -> bool {
77        true
78    }
79
80    /// Whether this provider supports IPv6
81    fn supports_v6(&self) -> bool {
82        false
83    }
84
85    /// Check if provider supports the given IP version
86    fn supports_version(&self, version: IpVersion) -> bool {
87        match version {
88            IpVersion::V4 => self.supports_v4(),
89            IpVersion::V6 => self.supports_v6(),
90            IpVersion::Any => self.supports_v4() || self.supports_v6(),
91        }
92    }
93
94    /// Get the public IP address asynchronously
95    fn get_ip(
96        &self,
97        version: IpVersion,
98    ) -> Pin<Box<dyn Future<Output = Result<IpAddr, ProviderError>> + Send + '_>>;
99}
100
101/// Type-erased asynchronous provider, used internally to store heterogeneous providers.
102#[cfg(feature = "tokio")]
103pub type BoxedProvider = Box<dyn Provider>;
104
105/// Stub provider returned when a protocol feature (dns/http/stun) is not enabled.
106/// Always returns an error explaining which feature is missing.
107#[derive(Clone)]
108pub(crate) struct DisabledProvider(pub(crate) String);
109
110impl BlockingProvider for DisabledProvider {
111    fn name(&self) -> &str {
112        &self.0
113    }
114
115    fn protocol(&self) -> Protocol {
116        Protocol::Http // doesn't matter, will always error
117    }
118
119    fn get_ip(&self, _version: IpVersion, _timeout: Duration) -> Result<IpAddr, ProviderError> {
120        Err(ProviderError::message(
121            &self.0,
122            "provider feature not enabled",
123        ))
124    }
125
126    fn clone_box(&self) -> BoxedBlockingProvider {
127        Box::new(self.clone())
128    }
129}
130
131#[cfg(feature = "tokio")]
132impl Provider for DisabledProvider {
133    fn name(&self) -> &str {
134        &self.0
135    }
136
137    fn protocol(&self) -> Protocol {
138        Protocol::Http // doesn't matter, will always error
139    }
140
141    fn get_ip(
142        &self,
143        _version: IpVersion,
144    ) -> Pin<Box<dyn Future<Output = Result<IpAddr, ProviderError>> + Send + '_>> {
145        let name = self.0.clone();
146        Box::pin(async move {
147            Err(ProviderError::message(
148                &name,
149                "provider feature not enabled",
150            ))
151        })
152    }
153}