@@ -21,6 +21,8 @@ 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
27
use getopts:: Options ;
26
28
use rustc_serialize:: json:: Json ;
@@ -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,13 @@ fn execute() -> i32 {
63
70
return success;
64
71
}
65
72
66
- match format_crate ( verbosity) {
73
+ let workspace_hitlist = match ( matches. opt_present ( "all" ) , matches. opt_present ( "p" ) ) {
74
+ ( false , false ) => WorkspaceHitlist :: None ,
75
+ ( true , _) => WorkspaceHitlist :: All ,
76
+ ( false , true ) => WorkspaceHitlist :: Some ( matches. opt_strs ( "p" ) ) ,
77
+ } ;
78
+
79
+ match format_crate ( verbosity, workspace_hitlist) {
67
80
Err ( e) => {
68
81
print_usage ( & opts, & e. to_string ( ) ) ;
69
82
failure
@@ -92,8 +105,10 @@ pub enum Verbosity {
92
105
Quiet ,
93
106
}
94
107
95
- fn format_crate ( verbosity : Verbosity ) -> Result < ExitStatus , std:: io:: Error > {
96
- let targets = try!( get_targets ( ) ) ;
108
+ fn format_crate ( verbosity : Verbosity ,
109
+ workspace_hitlist : WorkspaceHitlist )
110
+ -> Result < ExitStatus , std:: io:: Error > {
111
+ let targets = try!( get_targets ( workspace_hitlist) ) ;
97
112
98
113
// Currently only bin and lib files get formatted
99
114
let files: Vec < _ > = targets. into_iter ( )
@@ -139,28 +154,113 @@ pub struct Target {
139
154
kind : TargetKind ,
140
155
}
141
156
157
+ #[ derive( Debug ) ]
158
+ pub enum WorkspaceHitlist {
159
+ All ,
160
+ Some ( Vec < String > ) ,
161
+ None ,
162
+ }
163
+
164
+ impl WorkspaceHitlist {
165
+ fn is_all ( & self ) -> bool {
166
+ match * self {
167
+ WorkspaceHitlist :: All => true ,
168
+ _ => false ,
169
+ }
170
+ }
171
+
172
+ fn is_none ( & self ) -> bool {
173
+ match * self {
174
+ WorkspaceHitlist :: None => true ,
175
+ _ => false ,
176
+ }
177
+ }
178
+
179
+ pub fn get_some < ' a > ( & ' a self ) -> Option < & ' a [ String ] > {
180
+ use std:: borrow:: Borrow ;
181
+ match self {
182
+ & WorkspaceHitlist :: Some ( ref hitlist) => Some ( hitlist. borrow ( ) ) ,
183
+ _ => None ,
184
+ }
185
+ }
186
+ }
187
+
142
188
// Returns a vector of all compile targets of a crate
143
- fn get_targets ( ) -> Result < Vec < Target > , std:: io:: Error > {
189
+ fn get_targets ( workspace_hitlist : WorkspaceHitlist ) -> Result < Vec < Target > , std:: io:: Error > {
144
190
let mut targets: Vec < Target > = vec ! [ ] ;
145
- let output = try!( Command :: new ( "cargo" ) . arg ( "read-manifest" ) . output ( ) ) ;
191
+ if workspace_hitlist. is_none ( ) {
192
+ let output = try!( Command :: new ( "cargo" ) . arg ( "read-manifest" ) . output ( ) ) ;
193
+ if output. status . success ( ) {
194
+ // None of the unwraps should fail if output of `cargo read-manifest` is correct
195
+ let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
196
+ let json = Json :: from_str ( data) . unwrap ( ) ;
197
+ let jtargets = json. find ( "targets" )
198
+ . unwrap ( )
199
+ . as_array ( )
200
+ . unwrap ( ) ;
201
+ for jtarget in jtargets {
202
+ targets. push ( target_from_json ( jtarget) ) ;
203
+ }
204
+
205
+ return Ok ( targets) ;
206
+ }
207
+ return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
208
+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) ) ;
209
+ }
210
+ // This happens when cargo-fmt is not used inside a crate or
211
+ // is used inside a workspace.
212
+ // To ensure backward compatability, we only use `cargo metadata` for workspaces.
213
+ // TODO: How do we make sure we use either metadata or read-manifest
214
+ let output = Command :: new ( "cargo" ) . arg ( "metadata" )
215
+ . arg ( "--no-deps" )
216
+ . output ( ) ?;
146
217
if output. status . success ( ) {
147
- // None of the unwraps should fail if output of `cargo read-manifest` is correct
148
218
let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
149
219
let json = Json :: from_str ( data) . unwrap ( ) ;
150
- let jtargets = json. find ( "targets" )
220
+ let mut hitlist: HashSet < & String > = if !workspace_hitlist. is_all ( ) {
221
+ HashSet :: from_iter ( workspace_hitlist. get_some ( ) . unwrap ( ) )
222
+ } else {
223
+ HashSet :: new ( ) // Unused
224
+ } ;
225
+ let members: Vec < & Json > = json. find ( "packages" )
151
226
. unwrap ( )
152
227
. as_array ( )
153
- . unwrap ( ) ;
154
- for jtarget in jtargets {
155
- targets. push ( target_from_json ( jtarget) ) ;
228
+ . unwrap ( )
229
+ . into_iter ( )
230
+ . filter ( |member| if workspace_hitlist. is_all ( ) {
231
+ true
232
+ } else {
233
+ let member_name = member. find ( "name" )
234
+ . unwrap ( )
235
+ . as_string ( )
236
+ . unwrap ( ) ;
237
+ if hitlist. take ( & member_name. to_string ( ) ) . is_some ( ) {
238
+ true
239
+ } else {
240
+ false
241
+ }
242
+ } )
243
+ . collect ( ) ;
244
+ if hitlist. len ( ) != 0 {
245
+ // Mimick cargo of only outputting one <package> spec.
246
+ return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput ,
247
+ format ! ( "Could not find package: {:?}" ,
248
+ hitlist. iter( ) . next( ) . unwrap( ) ) ) ) ;
156
249
}
157
-
158
- Ok ( targets)
159
- } else {
160
- // This happens when cargo-fmt is not used inside a crate
161
- Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
162
- str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) )
250
+ for member in members {
251
+ let jtargets = member. find ( "targets" )
252
+ . unwrap ( )
253
+ . as_array ( )
254
+ . unwrap ( ) ;
255
+ for jtarget in jtargets {
256
+ targets. push ( target_from_json ( jtarget) ) ;
257
+ }
258
+ }
259
+ return Ok ( targets) ;
163
260
}
261
+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
262
+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) )
263
+
164
264
}
165
265
166
266
fn target_from_json ( jtarget : & Json ) -> Target {
0 commit comments