1use std::{
2 fmt,
3 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
4};
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Address {
13 Inet(IpAddr, u16),
14 Name(String, u16),
15}
16
17impl Address {
18 pub fn port(&self) -> u16 {
20 match self {
21 Self::Inet(_, p) => *p,
22 Self::Name(_, p) => *p,
23 }
24 }
25
26 pub fn set_port(&mut self, p: u16) {
28 match self {
29 Self::Inet(_, o) => *o = p,
30 Self::Name(_, o) => *o = p,
31 }
32 }
33
34 pub fn with_port(mut self, p: u16) -> Self {
35 match self {
36 Self::Inet(ip, _) => self = Self::Inet(ip, p),
37 Self::Name(hn, _) => self = Self::Name(hn, p),
38 }
39 self
40 }
41
42 pub fn with_offset(mut self, o: u16) -> Self {
43 match self {
44 Self::Inet(ip, p) => self = Self::Inet(ip, p + o),
45 Self::Name(hn, p) => self = Self::Name(hn, p + o),
46 }
47 self
48 }
49
50 pub fn is_ip(&self) -> bool {
51 matches!(self, Self::Inet(..))
52 }
53}
54
55impl fmt::Display for Address {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 Self::Inet(a, p) => write!(f, "{a}:{p}"),
59 Self::Name(h, p) => write!(f, "{h}:{p}"),
60 }
61 }
62}
63
64impl From<(&str, u16)> for Address {
65 fn from((h, p): (&str, u16)) -> Self {
66 Self::Name(h.to_string(), p)
67 }
68}
69
70impl From<(String, u16)> for Address {
71 fn from((h, p): (String, u16)) -> Self {
72 Self::Name(h, p)
73 }
74}
75
76impl From<(IpAddr, u16)> for Address {
77 fn from((ip, p): (IpAddr, u16)) -> Self {
78 Self::Inet(ip, p)
79 }
80}
81
82impl From<(Ipv4Addr, u16)> for Address {
83 fn from((ip, p): (Ipv4Addr, u16)) -> Self {
84 Self::Inet(IpAddr::V4(ip), p)
85 }
86}
87
88impl From<(Ipv6Addr, u16)> for Address {
89 fn from((ip, p): (Ipv6Addr, u16)) -> Self {
90 Self::Inet(IpAddr::V6(ip), p)
91 }
92}
93
94impl From<SocketAddr> for Address {
95 fn from(a: SocketAddr) -> Self {
96 Self::Inet(a.ip(), a.port())
97 }
98}
99
100impl std::str::FromStr for Address {
101 type Err = InvalidAddress;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 let parse = |a: &str, p: Option<&str>| {
105 let p: u16 = if let Some(p) = p {
106 p.parse().map_err(|_| InvalidAddress(()))?
107 } else {
108 0
109 };
110 IpAddr::from_str(a)
111 .map(|a| Self::Inet(a, p))
112 .or_else(|_| Ok(Self::Name(a.to_string(), p)))
113 };
114 match s.rsplit_once(':') {
115 None => parse(s, None),
116 Some((a, p)) => parse(a, Some(p)),
117 }
118 }
119}
120
121impl TryFrom<&str> for Address {
122 type Error = InvalidAddress;
123
124 fn try_from(val: &str) -> Result<Self, Self::Error> {
125 val.parse()
126 }
127}
128
129#[derive(Debug, Clone, thiserror::Error)]
130#[error("invalid address")]
131pub struct InvalidAddress(());
132
133impl Serialize for Address {
134 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
135 self.to_string().serialize(s)
136 }
137}
138
139impl<'de> Deserialize<'de> for Address {
140 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
141 let s = String::deserialize(d)?;
142 let a = s.parse().map_err(de::Error::custom)?;
143 Ok(a)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use std::net::IpAddr;
150
151 use super::Address;
152
153 #[test]
154 fn test_parse() {
155 let a: Address = "127.0.0.1:1234".parse().unwrap();
156 let Address::Inet(a, p) = a else {
157 unreachable!()
158 };
159 assert_eq!(IpAddr::from([127, 0, 0, 1]), a);
160 assert_eq!(1234, p);
161
162 let a: Address = "::1:1234".parse().unwrap();
163 let Address::Inet(a, p) = a else {
164 unreachable!()
165 };
166 assert_eq!("::1".parse::<IpAddr>().unwrap(), a);
167 assert_eq!(1234, p);
168
169 let a: Address = "localhost:1234".parse().unwrap();
170 let Address::Name(h, p) = a else {
171 unreachable!()
172 };
173 assert_eq!("localhost", &h);
174 assert_eq!(1234, p);
175
176 let a: Address = "sub.domain.com:1234".parse().unwrap();
177 let Address::Name(h, p) = a else {
178 unreachable!()
179 };
180 assert_eq!("sub.domain.com", &h);
181 assert_eq!(1234, p);
182 }
183}