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