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")]
18 extern crate rustc_serialize;
22 use std::path::PathBuf;
23 use std::process::{Command, ExitStatus};
27 use rustc_serialize::json::Json;
30 let exit_status = execute();
31 std::io::stdout().flush().unwrap();
32 std::process::exit(exit_status);
39 let mut opts = getopts::Options::new();
40 opts.optflag("h", "help", "show this message");
41 opts.optflag("q", "quiet", "no output printed to stdout");
42 opts.optflag("v", "verbose", "use verbose output");
44 let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
47 print_usage(&opts, &e.to_string());
52 let (verbose, quiet) = (matches.opt_present("v"), matches.opt_present("q"));
55 print_usage(&opts, "quiet mode and verbose mode are not compatible");
59 if matches.opt_present("h") {
60 print_usage(&opts, "");
64 match format_crate(verbose, quiet) {
66 print_usage(&opts, &e.to_string());
73 status.code().unwrap_or(failure)
79 fn print_usage(opts: &Options, reason: &str) {
80 let msg = format!("{}\nusage: cargo fmt [options]", reason);
81 println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
82 Arguments after `--` are passed to rustfmt.",
86 fn format_crate(verbose: bool, quiet: bool) -> Result<ExitStatus, std::io::Error> {
87 let targets = try!(get_targets());
89 // Currently only bin and lib files get formatted
90 let files: Vec<_> = targets.into_iter()
91 .filter(|t| t.kind.is_lib() | t.kind.is_bin())
94 println!("[{:?}] {:?}", t.kind, t.path)
100 format_files(&files, &get_fmt_args(), verbose, quiet)
103 fn get_fmt_args() -> Vec<String> {
104 // All arguments after -- are passed to rustfmt
105 env::args().skip_while(|a| a != "--").skip(1).collect()
110 Lib, // dylib, staticlib, lib
112 Other, // test, plugin,...
116 fn is_lib(&self) -> bool {
118 &TargetKind::Lib => true,
123 fn is_bin(&self) -> bool {
125 &TargetKind::Bin => true,
137 // Returns a vector of all compile targets of a crate
138 fn get_targets() -> Result<Vec<Target>, std::io::Error> {
139 let mut targets: Vec<Target> = vec![];
140 let output = try!(Command::new("cargo").arg("read-manifest").output());
141 if output.status.success() {
142 // None of the unwraps should fail if output of `cargo read-manifest` is correct
143 let data = &String::from_utf8(output.stdout).unwrap();
144 let json = Json::from_str(data).unwrap();
145 let jtargets = json.find("targets").unwrap().as_array().unwrap();
146 for jtarget in jtargets {
147 targets.push(target_from_json(jtarget));
152 // This happens when cargo-fmt is not used inside a crate
153 Err(std::io::Error::new(std::io::ErrorKind::NotFound,
154 str::from_utf8(&output.stderr).unwrap()))
158 fn target_from_json(jtarget: &Json) -> Target {
159 let jtarget = jtarget.as_object().unwrap();
160 let path = PathBuf::from(jtarget.get("src_path").unwrap().as_string().unwrap());
161 let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
162 let kind = match kinds[0].as_string().unwrap() {
163 "bin" => TargetKind::Bin,
164 "lib" | "dylib" | "staticlib" => TargetKind::Lib,
165 _ => TargetKind::Other,
174 fn format_files(files: &Vec<PathBuf>,
175 fmt_args: &Vec<String>,
178 -> Result<ExitStatus, std::io::Error> {
179 let stdout = if quiet {
180 std::process::Stdio::null()
182 std::process::Stdio::inherit()
186 for a in fmt_args.iter() {
189 for f in files.iter() {
190 print!(" {}", f.display());
194 let mut command = try!(Command::new("rustfmt")