Merge pull request #703 from JanLikar/cargo-fmt

Add cargo-fmt binary
This commit is contained in:
Nick Cameron 2015-12-16 17:51:29 +13:00
commit b37e78974c
3 changed files with 169 additions and 4 deletions

View file

@ -9,6 +9,10 @@ readme = "README.md"
license = "Apache-2.0/MIT"
include = ["src/*.rs", "Cargo.toml"]
[features]
default = ["cargo-fmt"]
cargo-fmt = []
[dependencies]
toml = "0.1.20"
rustc-serialize = "0.3.14"

View file

@ -21,14 +21,21 @@ or if you're using [`multirust`](https://github.com/brson/multirust)
multirust run nightly cargo install --git https://github.com/rust-lang-nursery/rustfmt
```
Usually cargo-fmt, which enables usage of Cargo subcommand `cargo fmt`, is
installed alongside rustfmt. To only install rustfmt run
```
cargo install --no-default-features --git https://github.com/rust-lang-nursery/rustfmt
```
## Running
You can run Rustfmt by just typing `rustfmt filename` if you used `Cargo
You can run Rustfmt by just typing `rustfmt filename` if you used `cargo
install`. This runs rustfmt on the given file, if the file includes out of line
modules, then we reformat those too. So to run on a whole module or crate, you
just need to run on the root file (usually mod.rs or lib.rs). Rustfmt can also
read data from stdin.
read data from stdin. Alternatively, you can use `cargo fmt` to format all
binary and library targets of your crate.
You'll probably want to specify the write mode. Currently, there are modes for
replace, overwrite, display, and coverage. The replace mode is the default
@ -42,6 +49,7 @@ screen, for example.
You can run `rustfmt --help` for more information.
`cargo fmt` uses `--write-mode=overwrite` by default.
## Running Rustfmt from your editor
@ -59,8 +67,8 @@ First make sure you've got Rust **1.4.0** or greater available, then:
`cargo test` to run all tests.
To run rustfmt after this, use `cargo run -- filename`. See the notes above on
running rustfmt.
To run rustfmt after this, use `cargo run --bin rustfmt -- filename`. See the
notes above on running rustfmt.
## What style does Rustfmt use?

153
src/bin/cargo-fmt.rs Normal file
View file

@ -0,0 +1,153 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 rustc_serialize;
use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::str;
use getopts::Options;
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).take_while(|a| a != "--")) {
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 bin and lib files of the current crate using rustfmt. \
Arguments after `--` are passes to rustfmt.",
opts.usage(&msg));
}
fn format_crate(opts: &Options) {
let targets = match get_targets() {
Ok(t) => t,
Err(e) => {
print_usage(opts, &e.to_string());
return;
}
};
// Currently only bin and lib files get formatted
let files: Vec<_> = targets.into_iter()
.filter(|t| t.kind.is_lib() | t.kind.is_bin())
.map(|t| t.path)
.collect();
format_files(&files, &get_fmt_args()).unwrap_or_else(|e| print_usage(opts, &e.to_string()));
}
fn get_fmt_args() -> Vec<String> {
let mut args = vec!["--write-mode=overwrite".to_string()];
// All arguments after -- are passed to rustfmt
args.extend(env::args().skip_while(|a| a != "--").skip(1));
args
}
#[derive(Debug)]
enum TargetKind {
Lib, // dylib, staticlib, lib
Bin, // bin
Other, // test, plugin,...
}
impl TargetKind {
fn is_lib(&self) -> bool {
match self {
&TargetKind::Lib => true,
_ => false,
}
}
fn is_bin(&self) -> bool {
match self {
&TargetKind::Bin => true,
_ => false,
}
}
}
#[derive(Debug)]
struct Target {
path: PathBuf,
kind: TargetKind,
}
// Returns a vector of all compile targets of a crate
fn get_targets() -> Result<Vec<Target>, std::io::Error> {
let mut targets: Vec<Target> = vec![];
let output = try!(Command::new("cargo").arg("read-manifest").output());
if output.status.success() {
// None of the unwraps should fail if output of `cargo read-manifest` is correct
let data = &String::from_utf8(output.stdout).unwrap();
let json = Json::from_str(data).unwrap();
let jtargets = json.find("targets").unwrap().as_array().unwrap();
for jtarget in jtargets {
targets.push(target_from_json(jtarget));
}
Ok(targets)
} 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 target_from_json(jtarget: &Json) -> Target {
let jtarget = jtarget.as_object().unwrap();
let path = PathBuf::from(jtarget.get("src_path").unwrap().as_string().unwrap());
let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
let kind = match kinds[0].as_string().unwrap() {
"bin" => TargetKind::Bin,
"lib" | "dylib" | "staticlib" => TargetKind::Lib,
_ => TargetKind::Other,
};
Target {
path: path,
kind: kind,
}
}
fn format_files(files: &Vec<PathBuf>, fmt_args: &Vec<String>) -> Result<(), std::io::Error> {
let mut command = try!(Command::new("rustfmt")
.args(files)
.args(fmt_args)
.spawn());
try!(command.wait());
Ok(())
}