From 623277e7e0583ad413a7fa0c886c65ebb5b4adc9 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Mon, 14 Dec 2015 15:50:19 +0100 Subject: [PATCH] Add cargo-fmt binary Add a new utility, which formats all readable .rs files in the src directory of the crate using rustfmt. Both binaries can be installed using cargo install rustfmt. cargo-fmt can be used as a Cargo subcommand - cargo fmt. --- src/bin/cargo-fmt.rs | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/bin/cargo-fmt.rs diff --git a/src/bin/cargo-fmt.rs b/src/bin/cargo-fmt.rs new file mode 100644 index 00000000000..9c7d8370548 --- /dev/null +++ b/src/bin/cargo-fmt.rs @@ -0,0 +1,116 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/) + +#![cfg(not(test))] +#![cfg(feature="cargo-fmt")] + +extern crate getopts; +extern crate walkdir; +extern crate rustc_serialize; + +use std::path::PathBuf; +use std::process::Command; +use std::env; +use std::str; + +use getopts::Options; +use walkdir::{WalkDir, DirEntry}; +use rustc_serialize::json::Json; + +fn main() { + let mut opts = getopts::Options::new(); + opts.optflag("h", "help", "show this message"); + + let matches = match opts.parse(env::args().skip(1)) { + Ok(m) => m, + Err(e) => { + print_usage(&opts, &e.to_string()); + return; + } + }; + + if matches.opt_present("h") { + print_usage(&opts, ""); + } else { + format_crate(&opts); + } +} + +fn print_usage(opts: &Options, reason: &str) { + let msg = format!("{}\nusage: cargo fmt [options]", reason); + println!("{}\nThis utility formats all readable .rs files in the src directory of the \ + current crate using rustfmt.", + opts.usage(&msg)); +} + +fn format_crate(opts: &Options) { + let mut root = match locate_root() { + Ok(r) => r, + Err(e) => { + print_usage(opts, &e.to_string()); + return; + } + }; + + // Currently only files in [root]/src can be formatted + root.push("src"); + // All unreadable or non .rs files are skipped + let files: Vec<_> = WalkDir::new(root) + .into_iter() + .filter(is_rs_file) + .filter_map(|f| f.ok()) + .map(|e| e.path().to_owned()) + .collect(); + + format_files(&files).unwrap_or_else(|e| print_usage(opts, &e.to_string())); +} + +fn locate_root() -> Result { + // This seems adequate, as cargo-fmt can only be used systems that have Cargo installed + let output = try!(Command::new("cargo").arg("locate-project").output()); + if output.status.success() { + // We assume cargo locate-project is not broken and + // it will output a valid json document + let data = &String::from_utf8(output.stdout).unwrap(); + let json = Json::from_str(data).unwrap(); + let root = PathBuf::from(json.find("root").unwrap().as_string().unwrap()); + + // root.parent() should never fail if locate-project's output is correct + Ok(root.parent().unwrap().to_owned()) + } else { + // This happens when cargo-fmt is not used inside a crate + Err(std::io::Error::new(std::io::ErrorKind::NotFound, + str::from_utf8(&output.stderr).unwrap())) + } +} + +fn is_rs_file(entry: &Result) -> bool { + match *entry { + Ok(ref file) => { + match file.path().extension() { + Some(ext) => ext == "rs", + None => false, + } + } + Err(_) => false, + } +} + +fn format_files(files: &Vec) -> Result<(), std::io::Error> { + let mut command = try!(Command::new("rustfmt") + .arg("--write-mode=overwrite") + .args(files) + .spawn()); + try!(command.wait()); + + Ok(()) +}