@@ -9,16 +9,23 @@ pub fn local_path_and_canonical_url(
9
9
url : & str ,
10
10
cargo_home : Option < & std:: path:: Path > ,
11
11
) -> Result < ( std:: path:: PathBuf , String ) , Error > {
12
- let ( dir_name, canonical_url) = url_to_local_dir ( url) ?;
13
-
14
12
let mut path = match cargo_home {
15
13
Some ( path) => path. to_owned ( ) ,
16
14
None => home:: cargo_home ( ) ?,
17
15
} ;
18
16
19
17
path. push ( "registry" ) ;
20
18
path. push ( "index" ) ;
21
- path. push ( dir_name) ;
19
+
20
+ let ( dir_name, canonical) = url_to_local_dir ( url, true ) ?;
21
+ let canonical_url = if path. join ( & dir_name) . exists ( ) {
22
+ path. push ( dir_name) ;
23
+ canonical
24
+ } else {
25
+ let ( dir_name, canonical) = url_to_local_dir ( url, false ) ?;
26
+ path. push ( dir_name) ;
27
+ canonical
28
+ } ;
22
29
23
30
Ok ( ( path, canonical_url) )
24
31
}
@@ -72,8 +79,8 @@ pub(crate) fn crate_name_to_relative_path(crate_name: &str, separator: Option<ch
72
79
73
80
/// Converts a full url, eg https://github.com/rust-lang/crates.io-index, into
74
81
/// the root directory name where cargo itself will fetch it on disk
75
- fn url_to_local_dir ( url : & str ) -> Result < ( String , String ) , Error > {
76
- fn to_hex ( num : u64 ) -> String {
82
+ fn url_to_local_dir ( url : & str , use_stable_hash : bool ) -> Result < ( String , String ) , Error > {
83
+ fn legacy_to_hex ( num : u64 ) -> String {
77
84
const CHARS : & [ u8 ] = b"0123456789abcdef" ;
78
85
79
86
let bytes = & [
@@ -102,7 +109,7 @@ fn url_to_local_dir(url: &str) -> Result<(String, String), Error> {
102
109
}
103
110
104
111
#[ allow( deprecated) ]
105
- fn hash_u64 ( url : & str , registry_kind : u64 ) -> u64 {
112
+ fn legacy_hash_u64 ( url : & str , registry_kind : u64 ) -> u64 {
106
113
use std:: hash:: { Hash , Hasher , SipHasher } ;
107
114
108
115
let mut hasher = SipHasher :: new_with_keys ( 0 , 0 ) ;
@@ -113,6 +120,35 @@ fn url_to_local_dir(url: &str) -> Result<(String, String), Error> {
113
120
hasher. finish ( )
114
121
}
115
122
123
+ fn stable_to_hex ( num : u64 ) -> String {
124
+ hex:: encode ( num. to_le_bytes ( ) )
125
+ }
126
+
127
+ fn stable_hash_u64 ( url : & str , registry_kind : u64 ) -> u64 {
128
+ // Matches https://github.com/rust-lang/cargo/blob/49e5d24870a13cd3f26ad61159045788b36efcdf/src/cargo/util/hasher.rs#L6
129
+ use rustc_stable_hash:: StableSipHasher128 as StableHasher ;
130
+ use std:: hash:: { Hash , Hasher } ;
131
+
132
+ let mut hasher = StableHasher :: new ( ) ;
133
+ // Registry
134
+ registry_kind. hash ( & mut hasher) ;
135
+ // Url
136
+ url. hash ( & mut hasher) ;
137
+ Hasher :: finish ( & hasher)
138
+ }
139
+
140
+ let hash_u64 = if use_stable_hash {
141
+ stable_hash_u64
142
+ } else {
143
+ legacy_hash_u64
144
+ } ;
145
+
146
+ let to_hex = if use_stable_hash {
147
+ stable_to_hex
148
+ } else {
149
+ legacy_to_hex
150
+ } ;
151
+
116
152
// SourceKind::Registry
117
153
let mut registry_kind = 2 ;
118
154
@@ -190,35 +226,62 @@ mod test {
190
226
fn http_index_url_matches_cargo ( ) {
191
227
use crate :: sparse:: URL ;
192
228
assert_eq ! (
193
- super :: url_to_local_dir( URL ) . unwrap( ) ,
229
+ super :: url_to_local_dir( URL , false ) . unwrap( ) ,
194
230
( "index.crates.io-6f17d22bba15001f" . to_owned( ) , URL . to_owned( ) , )
195
231
) ;
232
+ assert_eq ! (
233
+ super :: url_to_local_dir( URL , true ) . unwrap( ) ,
234
+ ( "index.crates.io-1949cf8c6b5b557f" . to_owned( ) , URL . to_owned( ) , )
235
+ ) ;
196
236
197
237
// I've confirmed this also works with a custom registry, unfortunately
198
238
// that one includes a secret key as part of the url which would allow
199
239
// anyone to publish to the registry, so uhh...here's a fake one instead
200
240
assert_eq ! (
201
- super :: url_to_local_dir( "https://dl.cloudsmith.io/aBcW1234aBcW1234/embark/rust/cargo/index.git" ) . unwrap( ) ,
241
+ super :: url_to_local_dir(
242
+ "https://dl.cloudsmith.io/aBcW1234aBcW1234/embark/rust/cargo/index.git" ,
243
+ false
244
+ )
245
+ . unwrap( ) ,
202
246
(
203
247
"dl.cloudsmith.io-ff79e51ddd2b38fd" . to_owned( ) ,
204
248
"https://dl.cloudsmith.io/aBcW1234aBcW1234/embark/rust/cargo/index.git" . to_owned( )
205
249
)
206
250
) ;
251
+ assert_eq ! (
252
+ super :: url_to_local_dir(
253
+ "https://dl.cloudsmith.io/aBcW1234aBcW1234/embark/rust/cargo/index.git" ,
254
+ true
255
+ )
256
+ . unwrap( ) ,
257
+ (
258
+ "dl.cloudsmith.io-fade991491aaf150" . to_owned( ) ,
259
+ "https://dl.cloudsmith.io/aBcW1234aBcW1234/embark/rust/cargo/index.git" . to_owned( )
260
+ )
261
+ ) ;
207
262
}
208
263
209
264
#[ test]
210
265
#[ cfg( feature = "git" ) ]
211
266
fn git_url_matches_cargo ( ) {
212
267
use crate :: git:: URL ;
213
268
assert_eq ! (
214
- crate :: dirs:: url_to_local_dir( URL ) . unwrap( ) ,
269
+ crate :: dirs:: url_to_local_dir( URL , false ) . unwrap( ) ,
270
+ ( "github.com-1ecc6299db9ec823" . to_owned( ) , URL . to_owned( ) )
271
+ ) ;
272
+ assert_eq ! (
273
+ crate :: dirs:: url_to_local_dir( URL , true ) . unwrap( ) ,
215
274
( "github.com-1ecc6299db9ec823" . to_owned( ) , URL . to_owned( ) )
216
275
) ;
217
276
218
277
// Ensure we actually strip off the irrelevant parts of a url, note that
219
278
// the .git suffix is not part of the canonical url, but *is* used when hashing
220
279
assert_eq ! (
221
- crate :: dirs:: url_to_local_dir( & format!( "registry+{}.git?one=1&two=2#fragment" , URL ) ) . unwrap( ) ,
280
+ crate :: dirs:: url_to_local_dir( & format!( "registry+{}.git?one=1&two=2#fragment" , URL ) , false ) . unwrap( ) ,
281
+ ( "github.com-c786010fb7ef2e6e" . to_owned( ) , URL . to_owned( ) )
282
+ ) ;
283
+ assert_eq ! (
284
+ crate :: dirs:: url_to_local_dir( & format!( "registry+{}.git?one=1&two=2#fragment" , URL ) , true ) . unwrap( ) ,
222
285
( "github.com-c786010fb7ef2e6e" . to_owned( ) , URL . to_owned( ) )
223
286
) ;
224
287
}
0 commit comments