@@ -6,6 +6,7 @@ use semver::Version;
6
6
use serde:: { de, ser} ;
7
7
use url:: Url ;
8
8
9
+ use crate :: core:: GitReference ;
9
10
use crate :: core:: PackageId ;
10
11
use crate :: core:: SourceKind ;
11
12
use crate :: util:: edit_distance;
@@ -104,17 +105,47 @@ impl PackageIdSpec {
104
105
name : String :: from ( package_id. name ( ) . as_str ( ) ) ,
105
106
version : Some ( package_id. version ( ) . clone ( ) . into ( ) ) ,
106
107
url : Some ( package_id. source_id ( ) . url ( ) . clone ( ) ) ,
107
- kind : None ,
108
+ kind : Some ( package_id . source_id ( ) . kind ( ) . clone ( ) ) ,
108
109
}
109
110
}
110
111
111
112
/// Tries to convert a valid `Url` to a `PackageIdSpec`.
112
113
fn from_url ( mut url : Url ) -> CargoResult < PackageIdSpec > {
114
+ let mut kind = None ;
115
+ if let Some ( ( kind_str, scheme) ) = url. scheme ( ) . split_once ( '+' ) {
116
+ match kind_str {
117
+ "git" => {
118
+ let git_ref = GitReference :: DefaultBranch ;
119
+ kind = Some ( SourceKind :: Git ( git_ref) ) ;
120
+ url = strip_url_protocol ( & url) ;
121
+ }
122
+ "registry" => {
123
+ kind = Some ( SourceKind :: Registry ) ;
124
+ url = strip_url_protocol ( & url) ;
125
+ }
126
+ "sparse" => {
127
+ kind = Some ( SourceKind :: SparseRegistry ) ;
128
+ // Leave `sparse` as part of URL, see `SourceId::new`
129
+ // url = strip_url_protocol(&url);
130
+ }
131
+ "path" => {
132
+ if scheme != "file" {
133
+ anyhow:: bail!( "`path+{scheme}` is unsupported; `path+file` and `file` schemes are supported" ) ;
134
+ }
135
+ kind = Some ( SourceKind :: Path ) ;
136
+ url = strip_url_protocol ( & url) ;
137
+ }
138
+ kind => anyhow:: bail!( "unsupported source protocol: {kind}" ) ,
139
+ }
140
+ }
141
+
113
142
if url. query ( ) . is_some ( ) {
114
143
bail ! ( "cannot have a query string in a pkgid: {}" , url)
115
144
}
145
+
116
146
let frag = url. fragment ( ) . map ( |s| s. to_owned ( ) ) ;
117
147
url. set_fragment ( None ) ;
148
+
118
149
let ( name, version) = {
119
150
let mut path = url
120
151
. path_segments ( )
@@ -148,7 +179,7 @@ impl PackageIdSpec {
148
179
name,
149
180
version,
150
181
url : Some ( url) ,
151
- kind : None ,
182
+ kind,
152
183
} )
153
184
}
154
185
@@ -173,6 +204,14 @@ impl PackageIdSpec {
173
204
self . url = Some ( url) ;
174
205
}
175
206
207
+ pub fn kind ( & self ) -> Option < & SourceKind > {
208
+ self . kind . as_ref ( )
209
+ }
210
+
211
+ pub fn set_kind ( & mut self , kind : SourceKind ) {
212
+ self . kind = Some ( kind) ;
213
+ }
214
+
176
215
/// Checks whether the given `PackageId` matches the `PackageIdSpec`.
177
216
pub fn matches ( & self , package_id : PackageId ) -> bool {
178
217
if self . name ( ) != package_id. name ( ) . as_str ( ) {
@@ -191,6 +230,12 @@ impl PackageIdSpec {
191
230
}
192
231
}
193
232
233
+ if let Some ( k) = & self . kind {
234
+ if k != package_id. source_id ( ) . kind ( ) {
235
+ return false ;
236
+ }
237
+ }
238
+
194
239
true
195
240
}
196
241
@@ -287,11 +332,20 @@ impl PackageIdSpec {
287
332
}
288
333
}
289
334
335
+ fn strip_url_protocol ( url : & Url ) -> Url {
336
+ // Ridiculous hoop because `Url::set_scheme` errors when changing to http/https
337
+ let raw = url. to_string ( ) ;
338
+ raw. split_once ( '+' ) . unwrap ( ) . 1 . parse ( ) . unwrap ( )
339
+ }
340
+
290
341
impl fmt:: Display for PackageIdSpec {
291
342
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
292
343
let mut printed_name = false ;
293
344
match self . url {
294
345
Some ( ref url) => {
346
+ if let Some ( protocol) = self . kind . as_ref ( ) . and_then ( |k| k. protocol ( ) ) {
347
+ write ! ( f, "{protocol}+" ) ?;
348
+ }
295
349
write ! ( f, "{}" , url) ?;
296
350
if url. path_segments ( ) . unwrap ( ) . next_back ( ) . unwrap ( ) != & * self . name {
297
351
printed_name = true ;
@@ -332,7 +386,7 @@ impl<'de> de::Deserialize<'de> for PackageIdSpec {
332
386
#[ cfg( test) ]
333
387
mod tests {
334
388
use super :: PackageIdSpec ;
335
- use crate :: core:: { PackageId , SourceId } ;
389
+ use crate :: core:: { GitReference , PackageId , SourceId , SourceKind } ;
336
390
use url:: Url ;
337
391
338
392
#[ test]
@@ -407,6 +461,26 @@ mod tests {
407
461
} ,
408
462
"https://crates.io/foo#[email protected] " ,
409
463
) ;
464
+ ok (
465
+ "registry+https://crates.io/foo#[email protected] " ,
466
+ PackageIdSpec {
467
+ name : String :: from ( "bar" ) ,
468
+ version : Some ( "1.2" . parse ( ) . unwrap ( ) ) ,
469
+ url : Some ( Url :: parse ( "https://crates.io/foo" ) . unwrap ( ) ) ,
470
+ kind : Some ( SourceKind :: Registry ) ,
471
+ } ,
472
+ "registry+https://crates.io/foo#[email protected] " ,
473
+ ) ;
474
+ ok (
475
+ "sparse+https://crates.io/foo#[email protected] " ,
476
+ PackageIdSpec {
477
+ name : String :: from ( "bar" ) ,
478
+ version : Some ( "1.2" . parse ( ) . unwrap ( ) ) ,
479
+ url : Some ( Url :: parse ( "sparse+https://crates.io/foo" ) . unwrap ( ) ) ,
480
+ kind : Some ( SourceKind :: SparseRegistry ) ,
481
+ } ,
482
+ "sparse+https://crates.io/foo#[email protected] " ,
483
+ ) ;
410
484
ok (
411
485
"foo" ,
412
486
PackageIdSpec {
@@ -499,6 +573,18 @@ mod tests {
499
573
} ,
500
574
"https://github.com/rust-lang/crates.io-index#[email protected] " ,
501
575
) ;
576
+ ok (
577
+ "sparse+https://github.com/rust-lang/crates.io-index#[email protected] " ,
578
+ PackageIdSpec {
579
+ name : String :: from ( "regex" ) ,
580
+ version : Some ( "1.4.3" . parse ( ) . unwrap ( ) ) ,
581
+ url : Some (
582
+ Url :: parse ( "sparse+https://github.com/rust-lang/crates.io-index" ) . unwrap ( ) ,
583
+ ) ,
584
+ kind : Some ( SourceKind :: SparseRegistry ) ,
585
+ } ,
586
+ "sparse+https://github.com/rust-lang/crates.io-index#[email protected] " ,
587
+ ) ;
502
588
ok (
503
589
"https://github.com/rust-lang/cargo#0.52.0" ,
504
590
PackageIdSpec {
@@ -529,6 +615,16 @@ mod tests {
529
615
} ,
530
616
531
617
) ;
618
+ ok (
619
+ "git+ssh://[email protected] /rust-lang/regex.git#[email protected] " ,
620
+ PackageIdSpec {
621
+ name : String :: from ( "regex" ) ,
622
+ version : Some ( "1.4.3" . parse ( ) . unwrap ( ) ) ,
623
+ url : Some ( Url :: parse ( "ssh://[email protected] /rust-lang/regex.git" ) . unwrap ( ) ) ,
624
+ kind : Some ( SourceKind :: Git ( GitReference :: DefaultBranch ) ) ,
625
+ } ,
626
+ "git+ssh://[email protected] /rust-lang/regex.git#[email protected] " ,
627
+ ) ;
532
628
ok (
533
629
"file:///path/to/my/project/foo" ,
534
630
PackageIdSpec {
@@ -549,6 +645,16 @@ mod tests {
549
645
} ,
550
646
"file:///path/to/my/project/foo#1.1.8" ,
551
647
) ;
648
+ ok (
649
+ "path+file:///path/to/my/project/foo#1.1.8" ,
650
+ PackageIdSpec {
651
+ name : String :: from ( "foo" ) ,
652
+ version : Some ( "1.1.8" . parse ( ) . unwrap ( ) ) ,
653
+ url : Some ( Url :: parse ( "file:///path/to/my/project/foo" ) . unwrap ( ) ) ,
654
+ kind : Some ( SourceKind :: Path ) ,
655
+ } ,
656
+ "path+file:///path/to/my/project/foo#1.1.8" ,
657
+ ) ;
552
658
}
553
659
554
660
#[ test]
@@ -560,6 +666,10 @@ mod tests {
560
666
assert ! ( PackageIdSpec :: parse( "baz@^1.0" ) . is_err( ) ) ;
561
667
assert ! ( PackageIdSpec :: parse( "https://baz:1.0" ) . is_err( ) ) ;
562
668
assert ! ( PackageIdSpec :: parse( "https://#baz:1.0" ) . is_err( ) ) ;
669
+ assert ! (
670
+ PackageIdSpec :: parse( "foobar+https://github.com/rust-lang/crates.io-index" ) . is_err( )
671
+ ) ;
672
+ assert ! ( PackageIdSpec :: parse( "path+https://github.com/rust-lang/crates.io-index" ) . is_err( ) ) ;
563
673
}
564
674
565
675
#[ test]
@@ -581,6 +691,12 @@ mod tests {
581
691
assert ! ( !
PackageIdSpec :: parse
( "https://bob.com#[email protected] " )
582
692
. unwrap( )
583
693
. matches( foo) ) ;
694
+ assert ! ( PackageIdSpec :: parse
( "registry+https://example.com#[email protected] " )
695
+ . unwrap( )
696
+ . matches( foo) ) ;
697
+ assert ! ( !
PackageIdSpec :: parse
( "git+https://example.com#[email protected] " )
698
+ . unwrap( )
699
+ . matches( foo) ) ;
584
700
585
701
let meta = PackageId :: new ( "meta" , "1.2.3+hello" , sid) . unwrap ( ) ;
586
702
assert ! ( PackageIdSpec :: parse( "meta" ) . unwrap( ) . matches( meta) ) ;
0 commit comments