1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/)
14 #![cfg(feature="cargo-fmt")]
17 extern crate rustc_serialize;
19 use std::path::PathBuf;
20 use std::process::Command;
25 use rustc_serialize::json::Json;
28 let mut opts = getopts::Options::new();
29 opts.optflag("h", "help", "show this message");
30 opts.optflag("q", "quiet", "no output printed to stdout");
31 opts.optflag("v", "verbose", "use verbose output");
33 let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
36 print_usage(&opts, &e.to_string());
41 let (verbose, quiet) = (matches.opt_present("v"), matches.opt_present("q"));
44 print_usage(&opts, "quiet mode and verbose mode are not compatible");
48 if matches.opt_present("h") {
49 print_usage(&opts, "");
51 format_crate(&opts, verbose, quiet);
55 fn print_usage(opts: &Options, reason: &str) {
56 let msg = format!("{}\nusage: cargo fmt [options]", reason);
57 println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
58 Arguments after `--` are passed to rustfmt.",
62 fn format_crate(opts: &Options, verbose: bool, quiet: bool) {
63 let targets = match get_targets() {
66 print_usage(opts, &e.to_string());
71 // Currently only bin and lib files get formatted
72 let files: Vec<_> = targets.into_iter()
73 .filter(|t| t.kind.is_lib() | t.kind.is_bin())
76 println!("[{:?}] {:?}", t.kind, t.path)
82 format_files(&files, &get_fmt_args(), verbose, quiet)
83 .unwrap_or_else(|e| print_usage(opts, &e.to_string()));
86 fn get_fmt_args() -> Vec<String> {
87 // All arguments after -- are passed to rustfmt
88 env::args().skip_while(|a| a != "--").skip(1).collect()
93 Lib, // dylib, staticlib, lib
95 Other, // test, plugin,...
99 fn is_lib(&self) -> bool {
101 &TargetKind::Lib => true,
106 fn is_bin(&self) -> bool {
108 &TargetKind::Bin => true,
120 // Returns a vector of all compile targets of a crate
121 fn get_targets() -> Result<Vec<Target>, std::io::Error> {
122 let mut targets: Vec<Target> = vec![];
123 let output = try!(Command::new("cargo").arg("read-manifest").output());
124 if output.status.success() {
125 // None of the unwraps should fail if output of `cargo read-manifest` is correct
126 let data = &String::from_utf8(output.stdout).unwrap();
127 let json = Json::from_str(data).unwrap();
128 let jtargets = json.find("targets").unwrap().as_array().unwrap();
129 for jtarget in jtargets {
130 targets.push(target_from_json(jtarget));
135 // This happens when cargo-fmt is not used inside a crate
136 Err(std::io::Error::new(std::io::ErrorKind::NotFound,
137 str::from_utf8(&output.stderr).unwrap()))
141 fn target_from_json(jtarget: &Json) -> Target {
142 let jtarget = jtarget.as_object().unwrap();
143 let path = PathBuf::from(jtarget.get("src_path").unwrap().as_string().unwrap());
144 let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
145 let kind = match kinds[0].as_string().unwrap() {
146 "bin" => TargetKind::Bin,
147 "lib" | "dylib" | "staticlib" => TargetKind::Lib,
148 _ => TargetKind::Other,
157 fn format_files(files: &Vec<PathBuf>,
158 fmt_args: &Vec<String>,
161 -> Result<(), std::io::Error> {
162 let stdout = if quiet {
163 std::process::Stdio::null()
165 std::process::Stdio::inherit()
169 for a in fmt_args.iter() {
172 for f in files.iter() {
173 print!(" {}", f.display());
177 let mut command = try!(Command::new("rustfmt")
182 try!(command.wait());