@@ -21,6 +21,8 @@ use std::io::Write;
2121use std:: path:: PathBuf ;
2222use std:: process:: { Command , ExitStatus } ;
2323use std:: str;
24+ use std:: collections:: HashSet ;
25+ use std:: iter:: FromIterator ;
2426
2527use getopts:: Options ;
2628use rustc_serialize:: json:: Json ;
@@ -39,6 +41,11 @@ fn execute() -> i32 {
3941 opts. optflag ( "h" , "help" , "show this message" ) ;
4042 opts. optflag ( "q" , "quiet" , "no output printed to stdout" ) ;
4143 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)" ) ;
4249
4350 let matches = match opts. parse ( env:: args ( ) . skip ( 1 ) . take_while ( |a| a != "--" ) ) {
4451 Ok ( m) => m,
@@ -63,7 +70,13 @@ fn execute() -> i32 {
6370 return success;
6471 }
6572
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) {
6780 Err ( e) => {
6881 print_usage ( & opts, & e. to_string ( ) ) ;
6982 failure
@@ -92,8 +105,10 @@ pub enum Verbosity {
92105 Quiet ,
93106}
94107
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) ) ;
97112
98113 // Currently only bin and lib files get formatted
99114 let files: Vec < _ > = targets. into_iter ( )
@@ -139,28 +154,113 @@ pub struct Target {
139154 kind : TargetKind ,
140155}
141156
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+
142188// 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 > {
144190 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 ( ) ?;
146217 if output. status . success ( ) {
147- // None of the unwraps should fail if output of `cargo read-manifest` is correct
148218 let data = & String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
149219 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" )
151226 . unwrap ( )
152227 . 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( ) ) ) ) ;
156249 }
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) ;
163260 }
261+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound ,
262+ str:: from_utf8 ( & output. stderr ) . unwrap ( ) ) )
263+
164264}
165265
166266fn target_from_json ( jtarget : & Json ) -> Target {
0 commit comments