ruma_identifiers/
server_name.rs1use std::net::Ipv4Addr;
4
5#[repr(transparent)]
11#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct ServerName(str);
13
14opaque_identifier_validated!(ServerName, ruma_identifiers_validation::server_name::validate);
15
16impl ServerName {
17 pub fn host(&self) -> &str {
22 if let Some(end_of_ipv6) = self.0.find(']') {
23 &self.0[..=end_of_ipv6]
24 } else {
25 let end_of_host = self.0.find(':').unwrap_or(self.0.len());
27 &self.0[..end_of_host]
28 }
29 }
30
31 pub fn port(&self) -> Option<u16> {
33 #[allow(clippy::unnecessary_lazy_evaluations)]
34 let end_of_host = self
35 .0
36 .find(']')
37 .map(|i| i + 1)
38 .or_else(|| self.0.find(':'))
39 .unwrap_or_else(|| self.0.len());
40
41 (self.0.len() != end_of_host).then(|| {
42 assert!(self.as_bytes()[end_of_host] == b':');
43 self.0[end_of_host + 1..].parse().unwrap()
44 })
45 }
46
47 pub fn is_ip_literal(&self) -> bool {
49 self.host().parse::<Ipv4Addr>().is_ok() || self.0.starts_with('[')
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use std::convert::TryFrom;
56
57 use super::ServerName;
58
59 #[test]
60 fn ipv4_host() {
61 assert!(<&ServerName>::try_from("127.0.0.1").is_ok());
62 }
63
64 #[test]
65 fn ipv4_host_and_port() {
66 assert!(<&ServerName>::try_from("1.1.1.1:12000").is_ok());
67 }
68
69 #[test]
70 fn ipv6() {
71 assert!(<&ServerName>::try_from("[::1]").is_ok());
72 }
73
74 #[test]
75 fn ipv6_with_port() {
76 assert!(<&ServerName>::try_from("[1234:5678::abcd]:5678").is_ok());
77 }
78
79 #[test]
80 fn dns_name() {
81 assert!(<&ServerName>::try_from("example.com").is_ok());
82 }
83
84 #[test]
85 fn dns_name_with_port() {
86 assert!(<&ServerName>::try_from("ruma.io:8080").is_ok());
87 }
88
89 #[test]
90 fn empty_string() {
91 assert!(<&ServerName>::try_from("").is_err());
92 }
93
94 #[test]
95 fn invalid_ipv6() {
96 assert!(<&ServerName>::try_from("[test::1]").is_err());
97 }
98
99 #[test]
100 fn ipv4_with_invalid_port() {
101 assert!(<&ServerName>::try_from("127.0.0.1:").is_err());
102 }
103
104 #[test]
105 fn ipv6_with_invalid_port() {
106 assert!(<&ServerName>::try_from("[fe80::1]:100000").is_err());
107 assert!(<&ServerName>::try_from("[fe80::1]!").is_err());
108 }
109
110 #[test]
111 fn dns_name_with_invalid_port() {
112 assert!(<&ServerName>::try_from("matrix.org:hello").is_err());
113 }
114
115 #[test]
116 fn parse_ipv4_host() {
117 let server_name = <&ServerName>::try_from("127.0.0.1").unwrap();
118 assert!(server_name.is_ip_literal());
119 assert_eq!(server_name.host(), "127.0.0.1");
120 }
121
122 #[test]
123 fn parse_ipv4_host_and_port() {
124 let server_name = <&ServerName>::try_from("1.1.1.1:12000").unwrap();
125 assert!(server_name.is_ip_literal());
126 assert_eq!(server_name.host(), "1.1.1.1");
127 }
128
129 #[test]
130 fn parse_ipv6() {
131 let server_name = <&ServerName>::try_from("[::1]").unwrap();
132 assert!(server_name.is_ip_literal());
133 assert_eq!(server_name.host(), "[::1]");
134 }
135
136 #[test]
137 fn parse_ipv6_with_port() {
138 let server_name = <&ServerName>::try_from("[1234:5678::abcd]:5678").unwrap();
139 assert!(server_name.is_ip_literal());
140 assert_eq!(server_name.host(), "[1234:5678::abcd]");
141 }
142
143 #[test]
144 fn parse_dns_name() {
145 let server_name = <&ServerName>::try_from("example.com").unwrap();
146 assert!(!server_name.is_ip_literal());
147 assert_eq!(server_name.host(), "example.com");
148 }
149
150 #[test]
151 fn parse_dns_name_with_port() {
152 let server_name = <&ServerName>::try_from("ruma.io:8080").unwrap();
153 assert!(!server_name.is_ip_literal());
154 assert_eq!(server_name.host(), "ruma.io");
155 }
156}