1
1
use std:: collections:: hash_map:: DefaultHasher ;
2
+ use std:: fmt;
3
+ use std:: fmt:: { format, Formatter } ;
2
4
use std:: hash:: { Hash , Hasher } ;
5
+ use std:: ops:: Deref ;
3
6
4
7
use idna:: punycode:: decode_to_string;
5
8
use pyo3:: exceptions:: PyValueError ;
6
9
use pyo3:: once_cell:: GILOnceCell ;
7
10
use pyo3:: pyclass:: CompareOp ;
8
11
use pyo3:: types:: { PyDict , PyType } ;
9
12
use pyo3:: { intern, prelude:: * } ;
13
+ use url:: quirks:: username;
10
14
use url:: Url ;
11
15
12
16
use crate :: tools:: SchemaDict ;
@@ -373,30 +377,26 @@ impl PyMultiHostUrl {
373
377
password : Option < & str > ,
374
378
port : Option < & str > ,
375
379
) -> PyResult < & ' a PyAny > {
376
- // todo better error message
377
380
let mut url = if hosts. is_some ( ) && ( host. is_some ( ) || user. is_some ( ) || password. is_some ( ) || port. is_some ( ) ) {
378
- return Err ( PyValueError :: new_err ( "Only one of `host` or `hosts` may be set" ) ) ;
379
- } else if hosts. is_some ( ) {
381
+ return Err ( PyValueError :: new_err (
382
+ "expected one of `hosts` or singular values to be set." ,
383
+ ) ) ;
384
+ } else if let Some ( hosts) = hosts {
380
385
// check all of host / user / password / port empty
381
386
// build multi-host url
382
- for single_host in hosts . as_deref ( ) . unwrap_or_default ( ) {
383
- let mut multi_url = format ! ( "{scheme}://" ) ;
384
- if single_host. username . is_some ( ) && single_host . password . is_none ( )
385
- {
386
- multi_url . push_str ( * single_host . username ) ;
387
- multi_url . push ( '@' ) ;
387
+ let mut multi_url = format ! ( "{scheme}://" ) ;
388
+ for ( index , single_host ) in hosts . iter ( ) . enumerate ( ) {
389
+ if single_host. is_empty ( ) {
390
+ return Err ( PyValueError :: new_err (
391
+ "expected one of 'host', ' username', 'password' or 'port' to be set" ,
392
+ ) ) ;
388
393
}
389
- else if single_host. username . is_none ( ) && single_host. password . is_some ( )
390
- {
391
- multi_url. push_str ( * single_host. password ) ;
392
- multi_url. push ( '@' ) ;
393
- } ;
394
- if single_host. host
395
- else {
396
- return Err ( PyValueError :: new_err ( "Incomplete object." ) ) ;
394
+ multi_url. push_str ( & * single_host. to_string ( ) ) ;
395
+ if index != hosts. len ( ) - 1 {
396
+ multi_url. push ( ',' )
397
397
} ;
398
398
}
399
- todo ! ( )
399
+ multi_url
400
400
} else if let Some ( host) = host {
401
401
let user_password = match ( user, password) {
402
402
( Some ( user) , None ) => format ! ( "{user}@" ) ,
@@ -413,6 +413,7 @@ impl PyMultiHostUrl {
413
413
} else {
414
414
return Err ( PyValueError :: new_err ( "expected either `host` or `hosts` to be set" ) ) ;
415
415
} ;
416
+
416
417
if let Some ( path) = path {
417
418
url. push ( '/' ) ;
418
419
url. push_str ( path) ;
@@ -436,6 +437,12 @@ pub struct MultiHostUrlHost {
436
437
port : Option < String > ,
437
438
}
438
439
440
+ impl MultiHostUrlHost {
441
+ fn is_empty ( & self ) -> bool {
442
+ self . host . is_none ( ) && self . password . is_none ( ) && self . host . is_none ( ) && self . port . is_none ( )
443
+ }
444
+ }
445
+
439
446
impl FromPyObject < ' _ > for MultiHostUrlHost {
440
447
fn extract ( ob : & ' _ PyAny ) -> PyResult < Self > {
441
448
let py = ob. py ( ) ;
@@ -449,6 +456,26 @@ impl FromPyObject<'_> for MultiHostUrlHost {
449
456
}
450
457
}
451
458
459
+ impl fmt:: Display for MultiHostUrlHost {
460
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
461
+ let mut url = String :: new ( ) ;
462
+ if self . username . is_some ( ) && self . password . is_some ( ) {
463
+ url. push_str ( & * format ! (
464
+ "{}:{}" ,
465
+ self . username. as_ref( ) . unwrap( ) ,
466
+ self . password. as_ref( ) . unwrap( )
467
+ ) )
468
+ }
469
+ if self . host . is_some ( ) {
470
+ url. push_str ( & * format ! ( "@{}" , self . host. as_ref( ) . unwrap( ) ) )
471
+ }
472
+ if self . port . is_some ( ) {
473
+ url. push_str ( & * format ! ( ":{}" , self . port. as_ref( ) . unwrap( ) ) )
474
+ }
475
+ write ! ( f, "{}" , url)
476
+ }
477
+ }
478
+
452
479
fn host_to_dict < ' a > ( py : Python < ' a > , lib_url : & Url ) -> PyResult < & ' a PyDict > {
453
480
let dict = PyDict :: new ( py) ;
454
481
dict. set_item (
0 commit comments