@@ -21,8 +21,10 @@ use std::io::Write;
21
21
use std:: path:: PathBuf ;
22
22
use std:: process:: { Command , ExitStatus } ;
23
23
use std:: str;
24
+ use std:: collections:: HashSet ;
25
+ use std:: iter:: FromIterator ;
24
26
25
- use getopts:: Options ;
27
+ use getopts:: { Options , Matches } ;
26
28
use rustc_serialize:: json:: Json ;
27
29
28
30
fn main ( ) {
@@ -39,6 +41,11 @@ fn execute() -> i32 {
39
41
opts. optflag ( "h" , "help" , "show this message" ) ;
40
42
opts. optflag ( "q" , "quiet" , "no output printed to stdout" ) ;
41
43
opts. optflag ( "v" , "verbose" , "use verbose output" ) ;
44
+ opts. optmulti ( "p" ,
45
+ "package" ,
46
+ "specify package to format (only usable in workspaces)" ,
47
+ "<package>" ) ;
48
+ opts. optflag ( "" , "all" , "format all packages (only usable in workspaces)" ) ;
42
49
43
50
let matches = match opts. parse ( env:: args ( ) . skip ( 1 ) . take_while ( |a| a != "--" ) ) {
44
51
Ok ( m) => m,
@@ -63,7 +70,9 @@ fn execute() -> i32 {
63
70
return success;
64
71
}
65
72
66
- match format_crate ( verbosity) {
73
+ let workspace_hitlist = WorkspaceHitlist :: from_matches ( & matches) ;
74
+
75
+ match format_crate ( verbosity, workspace_hitlist) {
67
76
Err ( e) => {
68
77
print_usage ( & opts, & e. to_string ( ) ) ;
69
78
failure
@@ -92,8 +101,10 @@ pub enum Verbosity {
92
101
Quiet ,
93
102
}
94
103
95
- fn format_crate ( verbosity : Verbosity ) -> Result < ExitStatus , std:: io:: Error > {
96
- let targets = try!( get_targets ( ) ) ;
104
+ fn format_crate ( verbosity : Verbosity ,
105
+ workspace_hitlist : WorkspaceHitlist )
106
+ -> Result < ExitStatus , std:: io:: Error > {
107
+ let targets = try!( get_targets ( workspace_hitlist) ) ;
97
108
98
109
// Currently only bin and lib files get formatted
99
110
let files: Vec < _ > = targets
@@ -140,25 +151,94 @@ pub struct Target {
140
151
kind : TargetKind ,
141
152
}
142
153
154
+ #[ derive( Debug , PartialEq , Eq ) ]
155
+ pub enum WorkspaceHitlist {
156
+ All ,
157
+ Some ( Vec < String > ) ,
158
+ None ,
159
+ }
160
+
161
+ impl WorkspaceHitlist {
162
+ pub fn get_some < ' a > ( & ' a self ) -> Option < & ' a [ String ] > {
163
+ if let & WorkspaceHitlist :: Some ( ref hitlist) = self {
164
+ Some ( & hitlist)
165
+ } else {
166
+ None
167
+ }
168
+ }
169
+
170
+ pub fn from_matches ( matches : & Matches ) -> WorkspaceHitlist {
171
+ match ( matches. opt_present ( "all" ) , matches. opt_present ( "p" ) ) {
172
+ ( false , false ) => WorkspaceHitlist :: None ,
173
+ ( true , _) => WorkspaceHitlist :: All ,
174
+ ( false , true ) => WorkspaceHitlist :: Some ( matches. opt_strs ( "p" ) ) ,
175
+ }
176
+ }
177
+ }
178
+
143
179
// Returns a vector of all compile targets of a crate
144
- fn get_targets ( ) -> Result < Vec < Target > , std:: io:: Error > {
180
+ fn get_targets ( workspace_hitlist : WorkspaceHitlist ) -> Result < Vec < Target > , std:: io:: Error > {
145
181
let mut targets: Vec < Target > = vec ! [ ] ;
146
- let output = try!( Command :: new ( "cargo" ) . arg ( "read-manifest" ) . output ( ) ) ;
182
+ if workspace_hitlist == WorkspaceHitlist :: None {
183
+ let output = try!( Command :: new ( "cargo" ) . arg ( "read-manifest" ) . output ( ) ) ;
184
+ if output. status . success ( ) {
185
+ // None of the unwraps should fail if output of `cargo read-manifest` is correct
186
+ let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
187
+ let json = Json :: from_str ( data) . unwrap ( ) ;
188
+ let jtargets = json. find ( "targets" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
189
+ for jtarget in jtargets {
190
+ targets. push ( target_from_json ( jtarget) ) ;
191
+ }
192
+
193
+ return Ok ( targets) ;
194
+ }
195
+ return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
196
+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) ) ;
197
+ }
198
+ // This happens when cargo-fmt is not used inside a crate or
199
+ // is used inside a workspace.
200
+ // To ensure backward compatability, we only use `cargo metadata` for workspaces.
201
+ // TODO: Is it possible only use metadata or read-manifest
202
+ let output = Command :: new ( "cargo" ) . arg ( "metadata" )
203
+ . arg ( "--no-deps" )
204
+ . output ( ) ?;
147
205
if output. status . success ( ) {
148
- // None of the unwraps should fail if output of `cargo read-manifest` is correct
149
206
let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
150
207
let json = Json :: from_str ( data) . unwrap ( ) ;
151
- let jtargets = json. find ( "targets" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
152
- for jtarget in jtargets {
153
- targets. push ( target_from_json ( jtarget) ) ;
208
+ let mut hitlist: HashSet < & String > = if workspace_hitlist != WorkspaceHitlist :: All {
209
+ HashSet :: from_iter ( workspace_hitlist. get_some ( ) . unwrap ( ) )
210
+ } else {
211
+ HashSet :: new ( ) // Unused
212
+ } ;
213
+ let members: Vec < & Json > = json. find ( "packages" )
214
+ . unwrap ( )
215
+ . as_array ( )
216
+ . unwrap ( )
217
+ . into_iter ( )
218
+ . filter ( |member| if workspace_hitlist == WorkspaceHitlist :: All {
219
+ true
220
+ } else {
221
+ let member_name = member. find ( "name" ) . unwrap ( ) . as_string ( ) . unwrap ( ) ;
222
+ hitlist. take ( & member_name. to_string ( ) ) . is_some ( )
223
+ } )
224
+ . collect ( ) ;
225
+ if hitlist. len ( ) != 0 {
226
+ // Mimick cargo of only outputting one <package> spec.
227
+ return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput ,
228
+ format ! ( "package `{}` is not a member of the workspace" ,
229
+ hitlist. iter( ) . next( ) . unwrap( ) ) ) ) ;
154
230
}
155
-
156
- Ok ( targets)
157
- } else {
158
- // This happens when cargo-fmt is not used inside a crate
159
- Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
160
- str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) )
231
+ for member in members {
232
+ let jtargets = member. find ( "targets" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
233
+ for jtarget in jtargets {
234
+ targets. push ( target_from_json ( jtarget) ) ;
235
+ }
236
+ }
237
+ return Ok ( targets) ;
161
238
}
239
+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
240
+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) )
241
+
162
242
}
163
243
164
244
fn target_from_json ( jtarget : & Json ) -> Target {
0 commit comments