@@ -74,68 +74,204 @@ impl TryFrom<&'_ PackageId> for Crate {
74
74
type Error = failure:: Error ;
75
75
76
76
fn try_from ( pkgid : & PackageId ) -> Fallible < Crate > {
77
- let parts = & pkgid
78
- . repr
79
- . split_ascii_whitespace ( )
80
- . flat_map ( |s| {
81
- // remove ()
82
- s. trim_matches ( |c : char | c. is_ascii_punctuation ( ) )
83
- // split resource and protocol
84
- . split ( '+' )
85
- } )
86
- . collect :: < Vec < _ > > ( ) ;
87
-
88
- match parts[ ..] {
89
- [ name, version, "registry" , _] => Ok ( Crate :: Registry ( RegistryCrate {
90
- name : name. to_string ( ) ,
91
- version : version. to_string ( ) ,
92
- } ) ) ,
93
- [ _, _, "path" , path] => Ok ( Crate :: Path ( path. to_string ( ) ) ) ,
94
- [ _, _, "git" , repo] => {
95
- if repo. starts_with ( "https://github.com" ) {
96
- Ok ( Crate :: GitHub ( repo. replace ( '#' , "/" ) . parse ( ) ?) )
97
- } else {
98
- let mut parts = repo. split ( '#' ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
99
- let url = parts. pop ( ) ;
100
- let sha = parts. pop ( ) ;
101
-
102
- match ( url, sha) {
103
- ( Some ( url) , None ) => Ok ( Crate :: Git ( GitRepo {
104
- url : url. to_string ( ) ,
105
- sha : None ,
106
- } ) ) ,
107
- ( Some ( url) , Some ( sha) ) => Ok ( Crate :: Git ( GitRepo {
108
- // remove additional queries if the sha is present
109
- // as the crate version is already uniquely determined
110
- url : url. split ( '?' ) . next ( ) . unwrap ( ) . to_string ( ) ,
111
- sha : Some ( sha. to_string ( ) ) ,
112
- } ) ) ,
113
- _ => bail ! ( "malformed git repo: {}" , repo) ,
77
+ if pkgid. repr . contains ( |c : char | c. is_ascii_whitespace ( ) ) {
78
+ let parts = & pkgid
79
+ . repr
80
+ . split_ascii_whitespace ( )
81
+ . flat_map ( |s| {
82
+ // remove ()
83
+ s. trim_matches ( |c : char | c. is_ascii_punctuation ( ) )
84
+ // split resource and protocol
85
+ . split ( '+' )
86
+ } )
87
+ . collect :: < Vec < _ > > ( ) ;
88
+
89
+ match parts[ ..] {
90
+ [ name, version, "registry" , _] => Ok ( Crate :: Registry ( RegistryCrate {
91
+ name : name. to_string ( ) ,
92
+ version : version. to_string ( ) ,
93
+ } ) ) ,
94
+ [ _, _, "path" , path] => Ok ( Crate :: Path ( path. to_string ( ) ) ) ,
95
+ [ _, _, "git" , repo] => {
96
+ if repo. starts_with ( "https://github.com" ) {
97
+ Ok ( Crate :: GitHub ( repo. replace ( '#' , "/" ) . parse ( ) ?) )
98
+ } else {
99
+ let mut parts = repo. split ( '#' ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
100
+ let url = parts. pop ( ) ;
101
+ let sha = parts. pop ( ) ;
102
+
103
+ match ( url, sha) {
104
+ ( Some ( url) , None ) => Ok ( Crate :: Git ( GitRepo {
105
+ url : url. to_string ( ) ,
106
+ sha : None ,
107
+ } ) ) ,
108
+ ( Some ( url) , Some ( sha) ) => Ok ( Crate :: Git ( GitRepo {
109
+ // remove additional queries if the sha is present
110
+ // as the crate version is already uniquely determined
111
+ url : url. split ( '?' ) . next ( ) . unwrap ( ) . to_string ( ) ,
112
+ sha : Some ( sha. to_string ( ) ) ,
113
+ } ) ) ,
114
+ _ => bail ! ( "malformed git repo: {}" , repo) ,
115
+ }
114
116
}
115
117
}
118
+ _ => bail ! (
119
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
120
+ pkgid. repr
121
+ ) ,
116
122
}
117
- [ "registry" , url, ..] => {
118
- let Some ( ( _registry, krate) ) = url. split_once ( '#' ) else {
119
- bail ! (
120
- "malformed pkgid format: {}\n maybe the representation has changed?" ,
121
- pkgid. repr
122
- ) ;
123
+ } else if let Some ( ( kind_proto, host_path_query_anchor) ) = pkgid. repr . split_once ( "://" ) {
124
+ let ( kind, proto) = if let Some ( ( kind, proto) ) = kind_proto. split_once ( '+' ) {
125
+ ( Some ( kind) , proto)
126
+ } else {
127
+ ( None , kind_proto)
128
+ } ;
129
+
130
+ let ( host_path_query, anchor) =
131
+ if let Some ( ( host_path_query, anchor) ) = host_path_query_anchor. split_once ( '#' ) {
132
+ ( host_path_query, Some ( anchor) )
133
+ } else {
134
+ ( host_path_query_anchor, None )
123
135
} ;
124
- let Some ( ( crate_name, crate_version) ) = krate. split_once ( '@' ) else {
125
- bail ! (
126
- "malformed pkgid format: {}\n maybe the representation has changed?" ,
127
- pkgid. repr
128
- ) ;
136
+
137
+ let ( host_path, query) =
138
+ if let Some ( ( host_path, query) ) = host_path_query. split_once ( '?' ) {
139
+ ( host_path, Some ( query) )
140
+ } else {
141
+ ( host_path_query, None )
129
142
} ;
130
- Ok ( Crate :: Registry ( RegistryCrate {
131
- name : crate_name. to_string ( ) ,
132
- version : crate_version. to_string ( ) ,
133
- } ) )
143
+
144
+ match ( kind, proto, query) {
145
+ ( Some ( "path" ) | None , "file" , None ) => Ok ( Crate :: Path ( host_path. to_string ( ) ) ) ,
146
+ ( Some ( "registry" ) , _, None ) => {
147
+ if let Some ( anchor) = anchor {
148
+ if let Some ( ( package_name, version) ) = anchor. split_once ( [ '@' , ':' ] ) {
149
+ Ok ( Crate :: Registry ( RegistryCrate {
150
+ name : package_name. to_string ( ) ,
151
+ version : version. to_string ( ) ,
152
+ } ) )
153
+ } else if !anchor
154
+ . contains ( |c : char | !c. is_ascii_alphanumeric ( ) && c != '_' && c != '-' )
155
+ {
156
+ // anchor appears to be a valid package name
157
+ Ok ( Crate :: Registry ( RegistryCrate {
158
+ name : anchor. to_string ( ) ,
159
+ version : "unknown" . to_string ( ) ,
160
+ } ) )
161
+ } else {
162
+ // as the anchor is not a valid package name check whether it can be a version
163
+ let Some ( minor_major_path) = anchor. split ( [ '-' , '+' ] ) . next ( ) else {
164
+ bail ! ( "split always returns at least one element" )
165
+ } ;
166
+
167
+ if minor_major_path. split ( '.' ) . count ( ) > 3
168
+ || minor_major_path
169
+ . split ( '.' )
170
+ . any ( |part| part. contains ( |c : char | !c. is_ascii_digit ( ) ) )
171
+ {
172
+ bail ! (
173
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
174
+ pkgid. repr
175
+ )
176
+ }
177
+
178
+ let Some ( package_name) = host_path. split ( '/' ) . last ( ) else {
179
+ bail ! (
180
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
181
+ pkgid. repr
182
+ )
183
+ } ;
184
+
185
+ Ok ( Crate :: Registry ( RegistryCrate {
186
+ name : package_name. to_string ( ) ,
187
+ version : anchor. to_string ( ) ,
188
+ } ) )
189
+ }
190
+ } else {
191
+ bail ! (
192
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
193
+ pkgid. repr
194
+ )
195
+ }
196
+ }
197
+ ( None , "http" | "https" , _) | ( Some ( "git" ) , _, _)
198
+ if host_path. starts_with ( "github.com/" ) =>
199
+ {
200
+ let mut parts = host_path. split ( '/' ) . skip ( 1 ) ;
201
+ let Some ( org) = parts. next ( ) else {
202
+ bail ! (
203
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
204
+ pkgid. repr
205
+ )
206
+ } ;
207
+
208
+ let name = if let Some ( ( package_name, _version) ) =
209
+ anchor. and_then ( |anchor| anchor. split_once ( [ '@' , ':' ] ) )
210
+ {
211
+ package_name
212
+ } else if let Some ( name) = parts. next ( ) {
213
+ name
214
+ } else {
215
+ bail ! (
216
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
217
+ pkgid. repr
218
+ )
219
+ } ;
220
+
221
+ Ok ( Crate :: GitHub ( GitHubRepo {
222
+ org : org. to_string ( ) ,
223
+ name : name. to_string ( ) ,
224
+ sha : None ,
225
+ } ) )
226
+ }
227
+
228
+ ( Some ( "git" ) , _, None ) | ( None , "ssh" | "git" | "http" | "https" , None ) => {
229
+
230
+ let kind = if let Some ( kind) = kind { format ! { "{kind}+" } } else { String :: new ( ) } ;
231
+ Ok ( Crate :: Git ( GitRepo {
232
+ url : format ! ( "{kind}{proto}://{host_path}" ) ,
233
+ sha : None ,
234
+ } ) )
235
+ }
236
+ ( Some ( "git" ) , _, Some ( query) )
237
+ | ( None , "ssh" | "git" | "http" | "https" , Some ( query) ) => {
238
+
239
+ let Some ( ( query_kind, rev) ) = query. split_once ( '=' ) else {
240
+ bail ! (
241
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
242
+ pkgid. repr
243
+ )
244
+ } ;
245
+
246
+ let kind = if let Some ( kind) = kind { format ! { "{kind}+" } } else { String :: new ( ) } ;
247
+ match query_kind {
248
+ "branch" | "tag" | "rev" => Ok ( Crate :: Git ( GitRepo {
249
+ url : format ! ( "{kind}{proto}://{host_path}" ) ,
250
+ sha : Some ( rev. to_string ( ) ) ,
251
+ } ) ) ,
252
+ _ => {
253
+ bail ! (
254
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
255
+ pkgid. repr
256
+ )
257
+ }
258
+ }
259
+ }
260
+ _ => bail ! (
261
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
262
+ pkgid. repr
263
+ ) ,
134
264
}
135
- _ => bail ! (
136
- "malformed pkgid format: {}\n maybe the representation has changed?" ,
137
- pkgid. repr
138
- ) ,
265
+ } else if let Some ( ( package_name, version) ) = pkgid. repr . split_once ( [ '@' , ':' ] ) {
266
+ Ok ( Crate :: Registry ( RegistryCrate {
267
+ name : package_name. to_string ( ) ,
268
+ version : version. to_string ( ) ,
269
+ } ) )
270
+ } else {
271
+ Ok ( Crate :: Registry ( RegistryCrate {
272
+ name : pkgid. repr . clone ( ) ,
273
+ version : "unknown" . to_string ( ) ,
274
+ } ) )
139
275
}
140
276
}
141
277
}
@@ -270,16 +406,48 @@ mod tests {
270
406
. to_string( ) ,
271
407
sha: None
272
408
} ) ,
409
+
410
+ // package id spec <https://doc.rust-lang.org/cargo/reference/pkgid-spec.html>
411
+
273
412
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.15.0" => Crate :: Registry ( RegistryCrate {
274
413
name: "cookie" . to_string( ) ,
275
414
version: "0.15.0" . to_string( ) ,
276
415
} ) ,
277
- }
416
+ "regex@1.4.3" => Crate :: Registry ( RegistryCrate {
417
+ name: "regex" . to_string( ) ,
418
+ version: "1.4.3" . to_string( ) ,
419
+ } ) ,
278
420
279
- assert ! ( Crate :: try_from( & PackageId {
280
- repr: "invalid" . to_string( )
281
- } )
282
- . is_err( ) ) ;
421
+ "https://github.com/rust-lang/cargo#0.52.0" => Crate :: GitHub ( GitHubRepo {
422
+ org: "rust-lang" . to_string( ) ,
423
+ name: "cargo" . to_string( ) ,
424
+ sha: None
425
+ } ) ,
426
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2" => Crate :: GitHub ( GitHubRepo {
427
+ org: "rust-lang" . to_string( ) ,
428
+ name: "cargo-platform" . to_string( ) ,
429
+ sha: None
430
+ } ) ,
431
+
432
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate :: Git ( GitRepo {
433
+ url: "ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
434
+ sha: None
435
+ } ) ,
436
+ "git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate :: Git ( GitRepo {
437
+ url: "git+ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
438
+ sha: None
439
+ } ) ,
440
+ "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3" => Crate :: Git ( GitRepo {
441
+ url: "git+ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
442
+ sha: Some ( "dev" . to_string( ) )
443
+ } ) ,
444
+
445
+ "file:///path/to/my/project/foo" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
446
+ "file:///path/to/my/project/foo#1.1.8" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
447
+ "path+file:///path/to/my/project/foo#1.1.8" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
448
+
449
+ "invalid" => Crate :: Registry ( RegistryCrate { name: "invalid" . to_string( ) , version: "unknown" . to_string( ) } ) ,
450
+ }
283
451
}
284
452
285
453
#[ test]
0 commit comments