external_ip/sources/
interfaces.rs

1use std::fmt::Display;
2use std::future::Future;
3use std::net::IpAddr;
4use std::pin::Pin;
5
6/// IP Address family to try to resolve for
7#[derive(Copy, Clone, Debug, PartialEq, Default)]
8pub enum Family {
9    /// Doesn't provide a specific IP family, so it will try all of them
10    #[default]
11    Any,
12    /// Lookup only IPv4 addresses
13    IPv4,
14    /// Lookup only IPv6 addresses
15    IPv6,
16}
17
18#[derive(Debug, thiserror::Error)]
19pub enum Error {
20    #[error("HTTP request: {0}")]
21    Http(#[from] reqwest::Error),
22    #[error("Decode as UTF-8 failed: {0}")]
23    DecodeError(#[from] std::str::Utf8Error),
24    #[error("Address parsing: {0}")]
25    InvalidAddress(#[from] std::net::AddrParseError),
26    #[error("DNS resolution failed: {0}")]
27    Dns(#[from] hickory_resolver::error::ResolveError),
28    #[error("DNS resolution empty")]
29    DnsResolutionEmpty,
30    #[error("Unsupported family")]
31    UnsupportedFamily,
32    #[cfg(feature = "igd")]
33    #[error("IGD external IP: {0}")]
34    IgdExternalIp(#[from] igd::GetExternalIpError),
35    #[cfg(feature = "igd")]
36    #[error("IGD search {0}")]
37    IgdSearch(#[from] igd::SearchError),
38}
39
40pub type IpResult = Result<IpAddr, Error>;
41pub type IpFuture<'a> = Pin<Box<dyn Future<Output = IpResult> + Send + 'a>>;
42
43/// Interface for any kind of external ip source
44#[cfg_attr(test, mockall::automock)]
45pub trait Source: Display {
46    /// Returns a future that will represent the IP the source obtained
47    fn get_ip(&self, family: Family) -> IpFuture<'_>;
48
49    /// Clones the Source into a new Boxed trait object.
50    fn box_clone(&self) -> Box<dyn Source>;
51}
52
53#[cfg(test)]
54impl Display for MockSource {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "MockedSource")
57    }
58}
59
60impl Clone for Box<dyn Source> {
61    fn clone(&self) -> Box<dyn Source> {
62        self.box_clone()
63    }
64}