@@ -180,6 +180,29 @@ pub struct Target {
180
180
kind : TargetKind ,
181
181
}
182
182
183
+ impl Target {
184
+ pub fn from_json ( json_val : & Value ) -> Option < Self > {
185
+ let jtarget = json_val. as_object ( ) ?;
186
+ let path = PathBuf :: from ( jtarget. get ( "src_path" ) ?. as_str ( ) ?) ;
187
+ let kinds = jtarget. get ( "kind" ) ?. as_array ( ) ?;
188
+ let kind = match kinds[ 0 ] . as_str ( ) ? {
189
+ "bin" => TargetKind :: Bin ,
190
+ "lib" | "dylib" | "staticlib" | "cdylib" | "rlib" => TargetKind :: Lib ,
191
+ "test" => TargetKind :: Test ,
192
+ "example" => TargetKind :: Example ,
193
+ "bench" => TargetKind :: Bench ,
194
+ "custom-build" => TargetKind :: CustomBuild ,
195
+ "proc-macro" => TargetKind :: ProcMacro ,
196
+ _ => TargetKind :: Other ,
197
+ } ;
198
+
199
+ Some ( Target {
200
+ path : path,
201
+ kind : kind,
202
+ } )
203
+ }
204
+ }
205
+
183
206
#[ derive( Debug , PartialEq , Eq ) ]
184
207
pub enum WorkspaceHitlist {
185
208
All ,
@@ -205,76 +228,135 @@ impl WorkspaceHitlist {
205
228
}
206
229
}
207
230
208
- // Returns a vector of all compile targets of a crate
209
- fn get_targets ( workspace_hitlist : & WorkspaceHitlist ) -> Result < Vec < Target > , std:: io:: Error > {
231
+ fn get_cargo_metadata_from_utf8 ( v : & [ u8 ] ) -> Option < Value > {
232
+ json:: from_str ( str:: from_utf8 ( v) . ok ( ) ?) . ok ( )
233
+ }
234
+
235
+ fn get_json_array_with < ' a > ( v : & ' a Value , key : & str ) -> Option < & ' a Vec < Value > > {
236
+ v. as_object ( ) ?. get ( key) ?. as_array ( )
237
+ }
238
+
239
+ // `cargo metadata --no-deps | jq '.["packages"]'`
240
+ fn get_packages ( v : & [ u8 ] ) -> Result < Vec < Value > , io:: Error > {
241
+ let e = io:: Error :: new (
242
+ io:: ErrorKind :: NotFound ,
243
+ String :: from ( "`cargo metadata` returned json without a 'packages' key" ) ,
244
+ ) ;
245
+ match get_cargo_metadata_from_utf8 ( v) {
246
+ Some ( ref json_obj) => get_json_array_with ( json_obj, "packages" ) . cloned ( ) . ok_or ( e) ,
247
+ None => Err ( e) ,
248
+ }
249
+ }
250
+
251
+ fn extract_target_from_package ( package : & Value ) -> Option < Vec < Target > > {
252
+ let jtargets = get_json_array_with ( package, "targets" ) ?;
210
253
let mut targets: Vec < Target > = vec ! [ ] ;
254
+ for jtarget in jtargets {
255
+ targets. push ( Target :: from_json ( & jtarget) ?) ;
256
+ }
257
+ Some ( targets)
258
+ }
211
259
260
+ fn filter_packages_with_hitlist < ' a > (
261
+ packages : Vec < Value > ,
262
+ workspace_hitlist : & ' a WorkspaceHitlist ,
263
+ ) -> Result < Vec < Value > , & ' a String > {
264
+ if * workspace_hitlist == WorkspaceHitlist :: All {
265
+ return Ok ( packages) ;
266
+ }
267
+ let mut hitlist: HashSet < & String > = workspace_hitlist
268
+ . get_some ( )
269
+ . map_or ( HashSet :: new ( ) , HashSet :: from_iter) ;
270
+ let members: Vec < Value > = packages
271
+ . into_iter ( )
272
+ . filter ( |member| {
273
+ member
274
+ . as_object ( )
275
+ . and_then ( |member_obj| {
276
+ member_obj
277
+ . get ( "name" )
278
+ . and_then ( Value :: as_str)
279
+ . map ( |member_name| {
280
+ hitlist. take ( & member_name. to_string ( ) ) . is_some ( )
281
+ } )
282
+ } )
283
+ . unwrap_or ( false )
284
+ } )
285
+ . collect ( ) ;
286
+ if hitlist. is_empty ( ) {
287
+ Ok ( members)
288
+ } else {
289
+ Err ( hitlist. into_iter ( ) . next ( ) . unwrap ( ) )
290
+ }
291
+ }
292
+
293
+ fn get_dependencies_from_package ( package : & Value ) -> Option < Vec < PathBuf > > {
294
+ let jdependencies = get_json_array_with ( package, "dependencies" ) ?;
295
+ let root_path = env:: current_dir ( ) . ok ( ) ?;
296
+ let mut dependencies: Vec < PathBuf > = vec ! [ ] ;
297
+ for jdep in jdependencies {
298
+ let jdependency = jdep. as_object ( ) ?;
299
+ if !jdependency. get ( "source" ) ?. is_null ( ) {
300
+ continue ;
301
+ }
302
+ let name = jdependency. get ( "name" ) ?. as_str ( ) ?;
303
+ let mut path = root_path. clone ( ) ;
304
+ path. push ( & name) ;
305
+ dependencies. push ( path) ;
306
+ }
307
+ Some ( dependencies)
308
+ }
309
+
310
+ // Returns a vector of local dependencies under this crate
311
+ fn get_path_to_local_dependencies ( packages : & [ Value ] ) -> Vec < PathBuf > {
312
+ let mut local_dependencies: Vec < PathBuf > = vec ! [ ] ;
313
+ for package in packages {
314
+ if let Some ( mut d) = get_dependencies_from_package ( package) {
315
+ local_dependencies. append ( & mut d) ;
212
316
}
213
317
}
318
+ local_dependencies
319
+ }
320
+
321
+ // Returns a vector of all compile targets of a crate
322
+ fn get_targets ( workspace_hitlist : & WorkspaceHitlist ) -> Result < Vec < Target > , io:: Error > {
214
323
let output = Command :: new ( "cargo" )
215
- . arg ( "metadata" )
216
- . arg ( "--no-deps" )
324
+ . args ( & [ "metadata" , "--no-deps" , "--format-version=1" ] )
217
325
. output ( ) ?;
218
326
if output. status . success ( ) {
219
- let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
220
- let json: Value = json:: from_str ( data) . unwrap ( ) ;
221
- let json_obj = json. as_object ( ) . unwrap ( ) ;
222
- let mut hitlist: HashSet < & String > = if * workspace_hitlist != WorkspaceHitlist :: All {
223
- HashSet :: from_iter ( workspace_hitlist. get_some ( ) . unwrap ( ) )
224
- } else {
225
- HashSet :: new ( ) // Unused
226
- } ;
227
- let members: Vec < & Value > = json_obj
228
- . get ( "packages" )
229
- . unwrap ( )
230
- . as_array ( )
231
- . unwrap ( )
232
- . into_iter ( )
233
- . filter ( |member| if * workspace_hitlist == WorkspaceHitlist :: All {
234
- true
235
- } else {
236
- let member_obj = member. as_object ( ) . unwrap ( ) ;
237
- let member_name = member_obj. get ( "name" ) . unwrap ( ) . as_str ( ) . unwrap ( ) ;
238
- hitlist. take ( & member_name. to_string ( ) ) . is_some ( )
239
- } )
240
- . collect ( ) ;
241
- if !hitlist. is_empty ( ) {
242
- // Mimick cargo of only outputting one <package> spec.
243
- return Err ( std:: io:: Error :: new (
244
- std:: io:: ErrorKind :: InvalidInput ,
245
- format ! (
246
- "package `{}` is not a member of the workspace" ,
247
- hitlist. iter( ) . next( ) . unwrap( )
248
- ) ,
249
- ) ) ;
327
+ let cur_dir = env:: current_dir ( ) ?;
328
+ let mut targets: Vec < Target > = vec ! [ ] ;
329
+ let packages = get_packages ( & output. stdout ) ?;
330
+
331
+ // If we can find any local dependencies, we will try to get targets from those as well.
332
+ for path in get_path_to_local_dependencies ( & packages) {
333
+ env:: set_current_dir ( path) ?;
334
+ targets. append ( & mut get_targets ( workspace_hitlist) ?) ;
250
335
}
251
- for member in members {
252
- let member_obj = member. as_object ( ) . unwrap ( ) ;
253
- let jtargets = member_obj. get ( "targets" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
254
- for jtarget in jtargets {
255
- targets. push ( target_from_json ( jtarget) ) ;
336
+
337
+ env:: set_current_dir ( cur_dir) ?;
338
+ match filter_packages_with_hitlist ( packages, workspace_hitlist) {
339
+ Ok ( packages) => {
340
+ for package in packages {
341
+ if let Some ( mut target) = extract_target_from_package ( & package) {
342
+ targets. append ( & mut target) ;
343
+ }
344
+ }
345
+ Ok ( targets)
346
+ }
347
+ Err ( package) => {
348
+ // Mimick cargo of only outputting one <package> spec.
349
+ Err ( io:: Error :: new (
350
+ io:: ErrorKind :: InvalidInput ,
351
+ format ! ( "package `{}` is not a member of the workspace" , package) ,
352
+ ) )
256
353
}
257
354
}
258
- }
259
-
260
- fn target_from_json ( jtarget : & Value ) -> Target {
261
- let jtarget = jtarget. as_object ( ) . unwrap ( ) ;
262
- let path = PathBuf :: from ( jtarget. get ( "src_path" ) . unwrap ( ) . as_str ( ) . unwrap ( ) ) ;
263
- let kinds = jtarget. get ( "kind" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
264
- let kind = match kinds[ 0 ] . as_str ( ) . unwrap ( ) {
265
- "bin" => TargetKind :: Bin ,
266
- "lib" | "dylib" | "staticlib" | "cdylib" | "rlib" => TargetKind :: Lib ,
267
- "test" => TargetKind :: Test ,
268
- "example" => TargetKind :: Example ,
269
- "bench" => TargetKind :: Bench ,
270
- "custom-build" => TargetKind :: CustomBuild ,
271
- "proc-macro" => TargetKind :: ProcMacro ,
272
- _ => TargetKind :: Other ,
273
- } ;
274
-
275
- Target {
276
- path : path,
277
- kind : kind,
355
+ } else {
356
+ Err ( io:: Error :: new (
357
+ io:: ErrorKind :: NotFound ,
358
+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ,
359
+ ) )
278
360
}
279
361
}
280
362
0 commit comments