@@ -94,13 +94,8 @@ impl PackageIdSpec {
94
94
. into ( ) ) ;
95
95
}
96
96
}
97
- let mut parts = spec. splitn ( 2 , [ ':' , '@' ] ) ;
98
- let name = parts. next ( ) . unwrap ( ) ;
99
- let version = match parts. next ( ) {
100
- Some ( version) => Some ( version. parse :: < PartialVersion > ( ) ?) ,
101
- None => None ,
102
- } ;
103
- PackageName :: new ( name) ?;
97
+ let ( name, version) = parse_spec ( spec) ?. unwrap_or_else ( || ( spec. to_owned ( ) , None ) ) ;
98
+ PackageName :: new ( & name) ?;
104
99
Ok ( PackageIdSpec {
105
100
name : String :: from ( name) ,
106
101
version,
@@ -161,11 +156,8 @@ impl PackageIdSpec {
161
156
return Err ( ErrorKind :: MissingUrlPath ( url) . into ( ) ) ;
162
157
} ;
163
158
match frag {
164
- Some ( fragment) => match fragment. split_once ( [ ':' , '@' ] ) {
165
- Some ( ( name, part) ) => {
166
- let version = part. parse :: < PartialVersion > ( ) ?;
167
- ( String :: from ( name) , Some ( version) )
168
- }
159
+ Some ( fragment) => match parse_spec ( & fragment) ? {
160
+ Some ( ( name, ver) ) => ( name, ver) ,
169
161
None => {
170
162
if fragment. chars ( ) . next ( ) . unwrap ( ) . is_alphabetic ( ) {
171
163
( String :: from ( fragment. as_str ( ) ) , None )
@@ -217,6 +209,18 @@ impl PackageIdSpec {
217
209
}
218
210
}
219
211
212
+ fn parse_spec ( spec : & str ) -> Result < Option < ( String , Option < PartialVersion > ) > > {
213
+ let Some ( ( name, ver) ) = spec
214
+ . rsplit_once ( '@' )
215
+ . or_else ( || spec. rsplit_once ( ':' ) . filter ( |( n, _) | !n. ends_with ( ':' ) ) )
216
+ else {
217
+ return Ok ( None ) ;
218
+ } ;
219
+ let name = name. to_owned ( ) ;
220
+ let ver = ver. parse :: < PartialVersion > ( ) ?;
221
+ Ok ( Some ( ( name, Some ( ver) ) ) )
222
+ }
223
+
220
224
fn strip_url_protocol ( url : & Url ) -> Url {
221
225
// Ridiculous hoop because `Url::set_scheme` errors when changing to http/https
222
226
let raw = url. to_string ( ) ;
@@ -323,18 +327,30 @@ mod tests {
323
327
use crate :: core:: { GitReference , SourceKind } ;
324
328
use url:: Url ;
325
329
330
+ #[ track_caller]
331
+ fn ok ( spec : & str , expected : PackageIdSpec , expected_rendered : & str ) {
332
+ let parsed = PackageIdSpec :: parse ( spec) . unwrap ( ) ;
333
+ assert_eq ! ( parsed, expected) ;
334
+ let rendered = parsed. to_string ( ) ;
335
+ assert_eq ! ( rendered, expected_rendered) ;
336
+ let reparsed = PackageIdSpec :: parse ( & rendered) . unwrap ( ) ;
337
+ assert_eq ! ( reparsed, expected) ;
338
+ }
339
+
340
+ macro_rules! err {
341
+ ( $spec: expr, $expected: pat) => {
342
+ let err = PackageIdSpec :: parse( $spec) . unwrap_err( ) ;
343
+ let kind = err. 0 ;
344
+ assert!(
345
+ matches!( kind, $expected) ,
346
+ "`{}` parse error mismatch, got {kind:?}" ,
347
+ $spec
348
+ ) ;
349
+ } ;
350
+ }
351
+
326
352
#[ test]
327
353
fn good_parsing ( ) {
328
- #[ track_caller]
329
- fn ok ( spec : & str , expected : PackageIdSpec , expected_rendered : & str ) {
330
- let parsed = PackageIdSpec :: parse ( spec) . unwrap ( ) ;
331
- assert_eq ! ( parsed, expected) ;
332
- let rendered = parsed. to_string ( ) ;
333
- assert_eq ! ( rendered, expected_rendered) ;
334
- let reparsed = PackageIdSpec :: parse ( & rendered) . unwrap ( ) ;
335
- assert_eq ! ( reparsed, expected) ;
336
- }
337
-
338
354
ok (
339
355
"https://crates.io/foo" ,
340
356
PackageIdSpec {
@@ -425,6 +441,16 @@ mod tests {
425
441
} ,
426
442
"foo" ,
427
443
) ;
444
+ ok (
445
+ "foo::bar" ,
446
+ PackageIdSpec {
447
+ name : String :: from ( "foo::bar" ) ,
448
+ version : None ,
449
+ url : None ,
450
+ kind : None ,
451
+ } ,
452
+ "foo::bar" ,
453
+ ) ;
428
454
ok (
429
455
"foo:1.2.3" ,
430
456
PackageIdSpec {
@@ -435,6 +461,16 @@ mod tests {
435
461
} ,
436
462
437
463
) ;
464
+ ok (
465
+ "foo::bar:1.2.3" ,
466
+ PackageIdSpec {
467
+ name : String :: from ( "foo::bar" ) ,
468
+ version : Some ( "1.2.3" . parse ( ) . unwrap ( ) ) ,
469
+ url : None ,
470
+ kind : None ,
471
+ } ,
472
+
473
+ ) ;
438
474
ok (
439
475
440
476
PackageIdSpec {
@@ -445,6 +481,16 @@ mod tests {
445
481
} ,
446
482
447
483
) ;
484
+ ok (
485
+
486
+ PackageIdSpec {
487
+ name : String :: from ( "foo::bar" ) ,
488
+ version : Some ( "1.2.3" . parse ( ) . unwrap ( ) ) ,
489
+ url : None ,
490
+ kind : None ,
491
+ } ,
492
+
493
+ ) ;
448
494
ok (
449
495
450
496
PackageIdSpec {
@@ -579,6 +625,16 @@ mod tests {
579
625
} ,
580
626
"file:///path/to/my/project/foo" ,
581
627
) ;
628
+ ok (
629
+ "file:///path/to/my/project/foo::bar" ,
630
+ PackageIdSpec {
631
+ name : String :: from ( "foo::bar" ) ,
632
+ version : None ,
633
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo::bar" ) . unwrap ( ) ) ,
634
+ kind : None ,
635
+ } ,
636
+ "file:///path/to/my/project/foo::bar" ,
637
+ ) ;
582
638
ok (
583
639
"file:///path/to/my/project/foo#1.1.8" ,
584
640
PackageIdSpec {
@@ -599,29 +655,77 @@ mod tests {
599
655
} ,
600
656
"path+file:///path/to/my/project/foo#1.1.8" ,
601
657
) ;
658
+ ok (
659
+ "path+file:///path/to/my/project/foo#bar" ,
660
+ PackageIdSpec {
661
+ name : String :: from ( "bar" ) ,
662
+ version : None ,
663
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
664
+ kind : Some ( SourceKind :: Path ) ,
665
+ } ,
666
+ "path+file:///path/to/my/project/foo#bar" ,
667
+ ) ;
668
+ ok (
669
+ "path+file:///path/to/my/project/foo#foo::bar" ,
670
+ PackageIdSpec {
671
+ name : String :: from ( "foo::bar" ) ,
672
+ version : None ,
673
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
674
+ kind : Some ( SourceKind :: Path ) ,
675
+ } ,
676
+ "path+file:///path/to/my/project/foo#foo::bar" ,
677
+ ) ;
678
+ ok (
679
+ "path+file:///path/to/my/project/foo#bar:1.1.8" ,
680
+ PackageIdSpec {
681
+ name : String :: from ( "bar" ) ,
682
+ version : Some ( "1.1.8" . parse ( ) . unwrap ( ) ) ,
683
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
684
+ kind : Some ( SourceKind :: Path ) ,
685
+ } ,
686
+ "path+file:///path/to/my/project/foo#[email protected] " ,
687
+ ) ;
688
+ ok (
689
+ "path+file:///path/to/my/project/foo#foo::bar:1.1.8" ,
690
+ PackageIdSpec {
691
+ name : String :: from ( "foo::bar" ) ,
692
+ version : Some ( "1.1.8" . parse ( ) . unwrap ( ) ) ,
693
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
694
+ kind : Some ( SourceKind :: Path ) ,
695
+ } ,
696
+ "path+file:///path/to/my/project/foo#foo::[email protected] " ,
697
+ ) ;
698
+ ok (
699
+ "path+file:///path/to/my/project/foo#[email protected] " ,
700
+ PackageIdSpec {
701
+ name : String :: from ( "bar" ) ,
702
+ version : Some ( "1.1.8" . parse ( ) . unwrap ( ) ) ,
703
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
704
+ kind : Some ( SourceKind :: Path ) ,
705
+ } ,
706
+ "path+file:///path/to/my/project/foo#[email protected] " ,
707
+ ) ;
708
+ ok (
709
+ "path+file:///path/to/my/project/foo#foo::[email protected] " ,
710
+ PackageIdSpec {
711
+ name : String :: from ( "foo::bar" ) ,
712
+ version : Some ( "1.1.8" . parse ( ) . unwrap ( ) ) ,
713
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
714
+ kind : Some ( SourceKind :: Path ) ,
715
+ } ,
716
+ "path+file:///path/to/my/project/foo#foo::[email protected] " ,
717
+ ) ;
602
718
}
603
719
604
720
#[ test]
605
721
fn bad_parsing ( ) {
606
- macro_rules! err {
607
- ( $spec: expr, $expected: pat) => {
608
- let err = PackageIdSpec :: parse( $spec) . unwrap_err( ) ;
609
- let kind = err. 0 ;
610
- assert!(
611
- matches!( kind, $expected) ,
612
- "`{}` parse error mismatch, got {kind:?}" ,
613
- $spec
614
- ) ;
615
- } ;
616
- }
617
-
618
722
err ! ( "baz:" , ErrorKind :: PartialVersion ( _) ) ;
619
723
err ! ( "baz:*" , ErrorKind :: PartialVersion ( _) ) ;
620
724
err ! ( "baz@" , ErrorKind :: PartialVersion ( _) ) ;
621
725
err ! ( "baz@*" , ErrorKind :: PartialVersion ( _) ) ;
622
726
err ! ( "baz@^1.0" , ErrorKind :: PartialVersion ( _) ) ;
623
- err ! ( "https://baz:1.0" , ErrorKind :: PartialVersion ( _) ) ;
624
- err ! ( "https://#baz:1.0" , ErrorKind :: PartialVersion ( _) ) ;
727
+ err ! ( "https://baz:1.0" , ErrorKind :: NameValidation ( _) ) ;
728
+ err ! ( "https://#baz:1.0" , ErrorKind :: NameValidation ( _) ) ;
625
729
err ! (
626
730
"foobar+https://github.com/rust-lang/crates.io-index" ,
627
731
ErrorKind :: UnsupportedProtocol ( _)
0 commit comments