@@ -11,6 +11,7 @@ use std::{
1111 convert:: TryFrom ,
1212 fmt:: { self , Display , Formatter , Write } ,
1313 hash:: { Hash , Hasher } ,
14+ net:: Ipv6Addr ,
1415 path:: PathBuf ,
1516 str:: FromStr ,
1617 time:: Duration ,
@@ -128,9 +129,29 @@ impl<'de> Deserialize<'de> for ServerAddress {
128129 where
129130 D : Deserializer < ' de > ,
130131 {
131- let s: String = Deserialize :: deserialize ( deserializer) ?;
132- Self :: parse ( s. as_str ( ) )
133- . map_err ( |e| <D :: Error as serde:: de:: Error >:: custom ( format ! ( "{}" , e) ) )
132+ #[ derive( Deserialize ) ]
133+ #[ serde( untagged) ]
134+ enum ServerAddressHelper {
135+ String ( String ) ,
136+ Object { host : String , port : Option < u16 > } ,
137+ }
138+
139+ let helper = ServerAddressHelper :: deserialize ( deserializer) ?;
140+ match helper {
141+ ServerAddressHelper :: String ( string) => {
142+ Self :: parse ( string) . map_err ( serde:: de:: Error :: custom)
143+ }
144+ ServerAddressHelper :: Object { host, port } => {
145+ #[ cfg( unix) ]
146+ if host. ends_with ( "sock" ) {
147+ return Ok ( Self :: Unix {
148+ path : PathBuf :: from ( host) ,
149+ } ) ;
150+ }
151+
152+ Ok ( Self :: Tcp { host, port } )
153+ }
154+ }
134155 }
135156}
136157
@@ -185,74 +206,95 @@ impl FromStr for ServerAddress {
185206}
186207
187208impl ServerAddress {
188- /// Parses an address string into a `ServerAddress`.
209+ /// Parses an address string into a [ `ServerAddress`] .
189210 pub fn parse ( address : impl AsRef < str > ) -> Result < Self > {
190211 let address = address. as_ref ( ) ;
191- // checks if the address is a unix domain socket
192- #[ cfg( unix) ]
193- {
194- if address. ends_with ( ".sock" ) {
195- return Ok ( ServerAddress :: Unix {
212+
213+ if address. ends_with ( ".sock" ) {
214+ #[ cfg( unix) ]
215+ {
216+ let address = percent_decode ( address, "unix domain sockets must be URL-encoded" ) ?;
217+ return Ok ( Self :: Unix {
196218 path : PathBuf :: from ( address) ,
197219 } ) ;
198220 }
221+ #[ cfg( not( unix) ) ]
222+ return Err ( ErrorKind :: InvalidArgument {
223+ message : "unix domain sockets are not supported on this platform" . to_string ( ) ,
224+ }
225+ . into ( ) ) ;
199226 }
200- let mut parts = address. split ( ':' ) ;
201- let hostname = match parts. next ( ) {
202- Some ( part) => {
203- if part. is_empty ( ) {
204- return Err ( ErrorKind :: InvalidArgument {
205- message : format ! (
206- "invalid server address: \" {}\" ; hostname cannot be empty" ,
207- address
208- ) ,
209- }
210- . into ( ) ) ;
227+
228+ let ( hostname, port) = if let Some ( ip_literal) = address. strip_prefix ( "[" ) {
229+ let Some ( ( hostname, port) ) = ip_literal. split_once ( "]" ) else {
230+ return Err ( ErrorKind :: InvalidArgument {
231+ message : format ! (
232+ "invalid server address {}: missing closing ']' in IP literal hostname" ,
233+ address
234+ ) ,
211235 }
212- part
213- }
214- None => {
236+ . into ( ) ) ;
237+ } ;
238+
239+ if let Err ( parse_error) = Ipv6Addr :: from_str ( hostname) {
215240 return Err ( ErrorKind :: InvalidArgument {
216- message : format ! ( "invalid server address: \" {} \" " , address) ,
241+ message : format ! ( "invalid server address {}: {}" , address, parse_error ) ,
217242 }
218- . into ( ) )
243+ . into ( ) ) ;
219244 }
220- } ;
221245
222- let port = match parts. next ( ) {
223- Some ( part) => {
224- let port = u16:: from_str ( part) . map_err ( |_| ErrorKind :: InvalidArgument {
246+ let port = if port. is_empty ( ) {
247+ None
248+ } else if let Some ( port) = port. strip_prefix ( ":" ) {
249+ Some ( port)
250+ } else {
251+ return Err ( ErrorKind :: InvalidArgument {
225252 message : format ! (
226- "port must be valid 16-bit unsigned integer, instead got: {}" ,
227- part
253+ "invalid server address {}: the hostname can only be followed by a port \
254+ prefixed with ':', got {}",
255+ address, port
228256 ) ,
229- } ) ?;
230-
231- if port == 0 {
232- return Err ( ErrorKind :: InvalidArgument {
233- message : format ! (
234- "invalid server address: \" {}\" ; port must be non-zero" ,
235- address
236- ) ,
237- }
238- . into ( ) ) ;
239257 }
240- if parts. next ( ) . is_some ( ) {
258+ . into ( ) ) ;
259+ } ;
260+
261+ ( hostname, port)
262+ } else {
263+ match address. split_once ( ":" ) {
264+ Some ( ( hostname, port) ) => ( hostname, Some ( port) ) ,
265+ None => ( address, None ) ,
266+ }
267+ } ;
268+
269+ if hostname. is_empty ( ) {
270+ return Err ( ErrorKind :: InvalidArgument {
271+ message : format ! (
272+ "invalid server address {}: the hostname cannot be empty" ,
273+ address
274+ ) ,
275+ }
276+ . into ( ) ) ;
277+ }
278+
279+ let port = if let Some ( port) = port {
280+ match u16:: from_str ( port) {
281+ Ok ( 0 ) | Err ( _) => {
241282 return Err ( ErrorKind :: InvalidArgument {
242283 message : format ! (
243- "address \" {}\" contains more than one unescaped ':'" ,
244- address
284+ "invalid server address {}: the port must be an integer between 1 and \
285+ 65535, got {}",
286+ address, port
245287 ) ,
246288 }
247- . into ( ) ) ;
289+ . into ( ) )
248290 }
249-
250- Some ( port)
291+ Ok ( port) => Some ( port) ,
251292 }
252- None => None ,
293+ } else {
294+ None
253295 } ;
254296
255- Ok ( ServerAddress :: Tcp {
297+ Ok ( Self :: Tcp {
256298 host : hostname. to_lowercase ( ) ,
257299 port,
258300 } )
@@ -1165,6 +1207,7 @@ impl ClientOptions {
11651207 . iter ( )
11661208 . filter_map ( |addr| match addr {
11671209 ServerAddress :: Tcp { host, .. } => Some ( host. to_ascii_lowercase ( ) ) ,
1210+ #[ cfg( unix) ]
11681211 _ => None ,
11691212 } )
11701213 . collect ( )
@@ -1440,31 +1483,15 @@ impl ConnectionString {
14401483 None => ( None , None ) ,
14411484 } ;
14421485
1443- let mut host_list = Vec :: with_capacity ( hosts_section. len ( ) ) ;
1444- for host in hosts_section. split ( ',' ) {
1445- let address = if host. ends_with ( ".sock" ) {
1446- #[ cfg( unix) ]
1447- {
1448- ServerAddress :: parse ( percent_decode (
1449- host,
1450- "Unix domain sockets must be URL-encoded" ,
1451- ) ?)
1452- }
1453- #[ cfg( not( unix) ) ]
1454- return Err ( ErrorKind :: InvalidArgument {
1455- message : "Unix domain sockets are not supported on this platform" . to_string ( ) ,
1456- }
1457- . into ( ) ) ;
1458- } else {
1459- ServerAddress :: parse ( host)
1460- } ?;
1461- host_list. push ( address) ;
1462- }
1486+ let hosts = hosts_section
1487+ . split ( ',' )
1488+ . map ( ServerAddress :: parse)
1489+ . collect :: < Result < Vec < ServerAddress > > > ( ) ?;
14631490
14641491 let host_info = if !srv {
1465- HostInfo :: HostIdentifiers ( host_list )
1492+ HostInfo :: HostIdentifiers ( hosts )
14661493 } else {
1467- match & host_list [ ..] {
1494+ match & hosts [ ..] {
14681495 [ ServerAddress :: Tcp { host, port : None } ] => HostInfo :: DnsRecord ( host. clone ( ) ) ,
14691496 [ ServerAddress :: Tcp {
14701497 host : _,
0 commit comments