@@ -74,68 +74,210 @@ 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
+ let kind = if let Some ( kind) = kind {
230
+ format ! { "{kind}+" }
231
+ } else {
232
+ String :: new ( )
233
+ } ;
234
+ Ok ( Crate :: Git ( GitRepo {
235
+ url : format ! ( "{kind}{proto}://{host_path}" ) ,
236
+ sha : None ,
237
+ } ) )
238
+ }
239
+ ( Some ( "git" ) , _, Some ( query) )
240
+ | ( None , "ssh" | "git" | "http" | "https" , Some ( query) ) => {
241
+ let Some ( ( query_kind, rev) ) = query. split_once ( '=' ) else {
242
+ bail ! (
243
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
244
+ pkgid. repr
245
+ )
246
+ } ;
247
+
248
+ let kind = if let Some ( kind) = kind {
249
+ format ! { "{kind}+" }
250
+ } else {
251
+ String :: new ( )
252
+ } ;
253
+ match query_kind {
254
+ "branch" | "tag" | "rev" => Ok ( Crate :: Git ( GitRepo {
255
+ url : format ! ( "{kind}{proto}://{host_path}" ) ,
256
+ sha : Some ( rev. to_string ( ) ) ,
257
+ } ) ) ,
258
+ _ => {
259
+ bail ! (
260
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
261
+ pkgid. repr
262
+ )
263
+ }
264
+ }
265
+ }
266
+ _ => bail ! (
267
+ "malformed pkgid format: {}\n maybe the representation has changed?" ,
268
+ pkgid. repr
269
+ ) ,
134
270
}
135
- _ => bail ! (
136
- "malformed pkgid format: {}\n maybe the representation has changed?" ,
137
- pkgid. repr
138
- ) ,
271
+ } else if let Some ( ( package_name, version) ) = pkgid. repr . split_once ( [ '@' , ':' ] ) {
272
+ Ok ( Crate :: Registry ( RegistryCrate {
273
+ name : package_name. to_string ( ) ,
274
+ version : version. to_string ( ) ,
275
+ } ) )
276
+ } else {
277
+ Ok ( Crate :: Registry ( RegistryCrate {
278
+ name : pkgid. repr . clone ( ) ,
279
+ version : "unknown" . to_string ( ) ,
280
+ } ) )
139
281
}
140
282
}
141
283
}
@@ -270,16 +412,48 @@ mod tests {
270
412
. to_string( ) ,
271
413
sha: None
272
414
} ) ,
415
+
416
+ // package id spec <https://doc.rust-lang.org/cargo/reference/pkgid-spec.html>
417
+
273
418
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.15.0" => Crate :: Registry ( RegistryCrate {
274
419
name: "cookie" . to_string( ) ,
275
420
version: "0.15.0" . to_string( ) ,
276
421
} ) ,
277
- }
422
+ "regex@1.4.3" => Crate :: Registry ( RegistryCrate {
423
+ name: "regex" . to_string( ) ,
424
+ version: "1.4.3" . to_string( ) ,
425
+ } ) ,
278
426
279
- assert ! ( Crate :: try_from( & PackageId {
280
- repr: "invalid" . to_string( )
281
- } )
282
- . is_err( ) ) ;
427
+ "https://github.com/rust-lang/cargo#0.52.0" => Crate :: GitHub ( GitHubRepo {
428
+ org: "rust-lang" . to_string( ) ,
429
+ name: "cargo" . to_string( ) ,
430
+ sha: None
431
+ } ) ,
432
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2" => Crate :: GitHub ( GitHubRepo {
433
+ org: "rust-lang" . to_string( ) ,
434
+ name: "cargo-platform" . to_string( ) ,
435
+ sha: None
436
+ } ) ,
437
+
438
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate :: Git ( GitRepo {
439
+ url: "ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
440
+ sha: None
441
+ } ) ,
442
+ "git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate :: Git ( GitRepo {
443
+ url: "git+ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
444
+ sha: None
445
+ } ) ,
446
+ "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3" => Crate :: Git ( GitRepo {
447
+ url: "git+ssh://git@github.com/rust-lang/regex.git" . to_string( ) ,
448
+ sha: Some ( "dev" . to_string( ) )
449
+ } ) ,
450
+
451
+ "file:///path/to/my/project/foo" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
452
+ "file:///path/to/my/project/foo#1.1.8" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
453
+ "path+file:///path/to/my/project/foo#1.1.8" => Crate :: Path ( "/path/to/my/project/foo" . to_string( ) ) ,
454
+
455
+ "invalid" => Crate :: Registry ( RegistryCrate { name: "invalid" . to_string( ) , version: "unknown" . to_string( ) } ) ,
456
+ }
283
457
}
284
458
285
459
#[ test]
0 commit comments