@@ -94,13 +94,8 @@ impl PackageIdSpec {
9494 . into ( ) ) ;
9595 }
9696 }
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) ?;
10499 Ok ( PackageIdSpec {
105100 name : String :: from ( name) ,
106101 version,
@@ -161,11 +156,8 @@ impl PackageIdSpec {
161156 return Err ( ErrorKind :: MissingUrlPath ( url) . into ( ) ) ;
162157 } ;
163158 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) ,
169161 None => {
170162 if fragment. chars ( ) . next ( ) . unwrap ( ) . is_alphabetic ( ) {
171163 ( String :: from ( fragment. as_str ( ) ) , None )
@@ -217,6 +209,18 @@ impl PackageIdSpec {
217209 }
218210}
219211
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+
220224fn strip_url_protocol ( url : & Url ) -> Url {
221225 // Ridiculous hoop because `Url::set_scheme` errors when changing to http/https
222226 let raw = url. to_string ( ) ;
@@ -323,18 +327,30 @@ mod tests {
323327 use crate :: core:: { GitReference , SourceKind } ;
324328 use url:: Url ;
325329
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+
326352 #[ test]
327353 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-
338354 ok (
339355 "https://crates.io/foo" ,
340356 PackageIdSpec {
@@ -425,6 +441,16 @@ mod tests {
425441 } ,
426442 "foo" ,
427443 ) ;
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+ ) ;
428454 ok (
429455 "foo:1.2.3" ,
430456 PackageIdSpec {
@@ -435,6 +461,16 @@ mod tests {
435461 } ,
436462437463 ) ;
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+ ) ;
438474 ok (
439475440476 PackageIdSpec {
@@ -445,6 +481,16 @@ mod tests {
445481 } ,
446482447483 ) ;
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+ ) ;
448494 ok (
449495450496 PackageIdSpec {
@@ -579,6 +625,16 @@ mod tests {
579625 } ,
580626 "file:///path/to/my/project/foo" ,
581627 ) ;
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+ ) ;
582638 ok (
583639 "file:///path/to/my/project/foo#1.1.8" ,
584640 PackageIdSpec {
@@ -599,29 +655,77 @@ mod tests {
599655 } ,
600656 "path+file:///path/to/my/project/foo#1.1.8" ,
601657 ) ;
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+ ) ;
602718 }
603719
604720 #[ test]
605721 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-
618722 err ! ( "baz:" , ErrorKind :: PartialVersion ( _) ) ;
619723 err ! ( "baz:*" , ErrorKind :: PartialVersion ( _) ) ;
620724 err ! ( "baz@" , ErrorKind :: PartialVersion ( _) ) ;
621725 err ! ( "baz@*" , ErrorKind :: PartialVersion ( _) ) ;
622726 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 ( _) ) ;
625729 err ! (
626730 "foobar+https://github.com/rust-lang/crates.io-index" ,
627731 ErrorKind :: UnsupportedProtocol ( _)
0 commit comments