@@ -44,6 +44,10 @@ pub struct Opts {
44
44
#[ structopt( short = "p" , long = "package" , value_name = "package" ) ]
45
45
packages : Vec < String > ,
46
46
47
+ /// Specify a source file to format
48
+ #[ structopt( short = "s" , long = "src-file" , value_name = "src-file" ) ]
49
+ src_files : Vec < PathBuf > ,
50
+
47
51
/// Specify path to Cargo.toml
48
52
#[ structopt( long = "manifest-path" , value_name = "manifest-path" ) ]
49
53
manifest_path : Option < String > ,
@@ -89,6 +93,16 @@ fn execute() -> i32 {
89
93
90
94
let opts = Opts :: from_iter ( args) ;
91
95
96
+ if !opts. src_files . is_empty ( ) & !opts. packages . is_empty ( ) {
97
+ print_usage_to_stderr ( "cannot format source files and packages at the same time" ) ;
98
+ return FAILURE ;
99
+ }
100
+
101
+ if !opts. src_files . is_empty ( ) & opts. format_all {
102
+ print_usage_to_stderr ( "cannot format all packages when specifying source files" ) ;
103
+ return FAILURE ;
104
+ }
105
+
92
106
let verbosity = match ( opts. verbose , opts. quiet ) {
93
107
( false , false ) => Verbosity :: Normal ,
94
108
( false , true ) => Verbosity :: Quiet ,
@@ -314,17 +328,23 @@ pub enum CargoFmtStrategy {
314
328
/// Format every packages and dependencies.
315
329
All ,
316
330
/// Format packages that are specified by the command line argument.
317
- Some ( Vec < String > ) ,
331
+ Packages ( Vec < String > ) ,
318
332
/// Format the root packages only.
319
333
Root ,
334
+ /// Format individual source files specified by the command line arguments.
335
+ SourcFiles ( Vec < PathBuf > ) ,
320
336
}
321
337
322
338
impl CargoFmtStrategy {
323
339
pub fn from_opts ( opts : & Opts ) -> CargoFmtStrategy {
340
+ if !opts. src_files . is_empty ( ) {
341
+ return CargoFmtStrategy :: SourcFiles ( opts. src_files . clone ( ) ) ;
342
+ }
343
+
324
344
match ( opts. format_all , opts. packages . is_empty ( ) ) {
325
345
( false , true ) => CargoFmtStrategy :: Root ,
326
346
( true , _) => CargoFmtStrategy :: All ,
327
- ( false , false ) => CargoFmtStrategy :: Some ( opts. packages . clone ( ) ) ,
347
+ ( false , false ) => CargoFmtStrategy :: Packages ( opts. packages . clone ( ) ) ,
328
348
}
329
349
}
330
350
}
@@ -341,9 +361,12 @@ fn get_targets(
341
361
CargoFmtStrategy :: All => {
342
362
get_targets_recursive ( manifest_path, & mut targets, & mut BTreeSet :: new ( ) ) ?
343
363
}
344
- CargoFmtStrategy :: Some ( ref hitlist) => {
364
+ CargoFmtStrategy :: Packages ( ref hitlist) => {
345
365
get_targets_with_hitlist ( manifest_path, hitlist, & mut targets) ?
346
366
}
367
+ CargoFmtStrategy :: SourcFiles ( ref src_files) => {
368
+ get_targets_from_src_files ( manifest_path, & src_files, & mut targets) ?
369
+ }
347
370
}
348
371
349
372
if targets. is_empty ( ) {
@@ -356,6 +379,44 @@ fn get_targets(
356
379
}
357
380
}
358
381
382
+ fn get_targets_from_src_files (
383
+ manifest_path : Option < & Path > ,
384
+ src_files : & [ PathBuf ] ,
385
+ targets : & mut BTreeSet < Target > ,
386
+ ) -> Result < ( ) , io:: Error > {
387
+ let metadata = get_cargo_metadata ( manifest_path) ?;
388
+ let mut src_file_target_map: BTreeMap < PathBuf , ( String , String ) > = BTreeMap :: new ( ) ;
389
+
390
+ // Map each targt to it's parent directory.
391
+ for target in metadata. packages . iter ( ) . map ( |p| p. targets . iter ( ) ) . flatten ( ) {
392
+ let path = fs:: canonicalize ( & target. src_path ) ?
393
+ . parent ( )
394
+ . expect ( "Target src_path should have a parent directory" )
395
+ . to_owned ( ) ;
396
+
397
+ src_file_target_map. insert ( path, ( target. kind [ 0 ] . clone ( ) , target. edition . clone ( ) ) ) ;
398
+ }
399
+
400
+ // Given the path to a source file, traverse it's ancestors until we find one that's
401
+ // in the target map we just constructed, at which point we add it to the targets set.
402
+ // This ensures that we only support formatting arbitrary files within a package.
403
+ for file_path in src_files {
404
+ let canonicalize = fs:: canonicalize ( file_path) ?;
405
+ for path in canonicalize. ancestors ( ) {
406
+ if let Some ( ( kind, edition) ) = src_file_target_map. get ( path) {
407
+ targets. insert ( Target {
408
+ path : file_path. to_owned ( ) ,
409
+ kind : kind. clone ( ) ,
410
+ edition : edition. clone ( ) ,
411
+ } ) ;
412
+ break ;
413
+ }
414
+ }
415
+ }
416
+
417
+ Ok ( ( ) )
418
+ }
419
+
359
420
fn get_targets_root_only (
360
421
manifest_path : Option < & Path > ,
361
422
targets : & mut BTreeSet < Target > ,
0 commit comments