1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::Error;
use rsip_derives::NewType;
use std::convert::TryInto;
use std::hash::{Hash, Hasher};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

/// The `Host` enum represents the host part of the [HostWithPort](super::HostWithPort) struct
///
/// It has 2 variants:
///
/// * `Domain` that holds a [Domain] that represents a DNS domain.
/// * `IpAddr` that holds an [IpAddr](std::net::IpAddr) and represents a raw IP address
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Host {
    Domain(Domain),
    IpAddr(IpAddr),
}

impl Default for Host {
    fn default() -> Self {
        Self::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))
    }
}

impl std::str::FromStr for Host {
    type Err = crate::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.parse::<IpAddr>() {
            Ok(ip_addr) => Ok(Host::IpAddr(ip_addr)),
            Err(_) => Ok(Host::Domain(s.into())),
        }
    }
}

/// A NewType around `String` to hold DNS domains.
/// No check is done when you convert something into `Domain`.
#[derive(NewType, Debug, Eq, Clone)]
pub struct Domain(String);

impl Hash for Domain {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl PartialEq for Domain {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl std::fmt::Display for Host {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            Host::Domain(domain) => write!(f, "{}", domain),
            Host::IpAddr(ip_addr) => write!(f, "{}", ip_addr),
        }
    }
}

impl From<String> for Host {
    fn from(from: String) -> Self {
        from.as_str().into()
    }
}

impl From<&str> for Host {
    fn from(from: &str) -> Self {
        match from.parse::<IpAddr>() {
            Ok(ip_addr) => Host::IpAddr(ip_addr),
            Err(_) => Host::Domain(from.into()),
        }
    }
}

impl From<IpAddr> for Host {
    fn from(from: IpAddr) -> Self {
        Host::IpAddr(from)
    }
}

impl From<Domain> for Host {
    fn from(from: Domain) -> Self {
        Host::Domain(from)
    }
}

impl TryInto<IpAddr> for Host {
    type Error = Error;

    fn try_into(self) -> Result<IpAddr, Error> {
        match self {
            Self::Domain(_) => Err(Error::Unexpected("cannot convert Host to IpAddr".into())),
            Self::IpAddr(ip_addr) => Ok(ip_addr),
        }
    }
}

impl TryInto<SocketAddr> for Host {
    type Error = Error;

    fn try_into(self) -> Result<SocketAddr, Error> {
        let ip_addr: IpAddr = self.try_into()?;
        Ok(SocketAddr::new(ip_addr, 5060))
    }
}

#[cfg(feature = "test-utils")]
impl testing_utils::Randomize for Domain {
    fn random() -> Self {
        Domain(format!(
            "{}.{}",
            String::random(),
            testing_utils::rand_str_of(3)
        ))
    }
}

#[cfg(feature = "test-utils")]
impl testing_utils::Randomize for Host {
    fn random() -> Self {
        use testing_utils::sample;

        sample(&[
            Host::Domain(Domain::random()),
            Host::IpAddr(IpAddr::random()),
        ])
    }
}