From ec509b0517adb5a880a773988d514d1b76c5791b Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Mon, 14 Dec 2015 15:32:03 +0100 Subject: [PATCH 1/8] Change Cargo.toml Change Cargo.toml to include cargo-fmt's dependencies and add a default cargo-fmt feature to enable users to only build rustfmt if they don't need cargo-fmt. --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a3a167920484..c2d7ecf5a476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,10 @@ readme = "README.md" license = "Apache-2.0/MIT" include = ["src/*.rs", "Cargo.toml"] +[features] +default = ["cargo-fmt"] +cargo-fmt = ["walkdir"] + [dependencies] toml = "0.1.20" rustc-serialize = "0.3.14" @@ -21,3 +25,5 @@ syntex_syntax = "0.23.0" log = "0.3.2" env_logger = "0.3.1" getopts = "0.2" + +walkdir = {version = "0.1.5", optional = true} From 681245f3fb9d7b5564294bc765e435d30a8e1711 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Mon, 14 Dec 2015 15:49:46 +0100 Subject: [PATCH 2/8] Update Cargo.lock --- Cargo.lock | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 14afd0912485..73a16a224192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,7 @@ dependencies = [ "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -145,6 +146,15 @@ name = "unicode-xid" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "walkdir" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.5" From 623277e7e0583ad413a7fa0c886c65ebb5b4adc9 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Mon, 14 Dec 2015 15:50:19 +0100 Subject: [PATCH 3/8] 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 000000000000..9c7d83705486 --- /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(()) +} From 169bff0e526f6517305ba16c09ea4d8de72641ec Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Tue, 15 Dec 2015 01:18:47 +0100 Subject: [PATCH 4/8] Edit README.md - Add instructions for using cargo run with multiple binaries - Mention cargo-fmt binary --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d79e53b7a328..7347499c824e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ 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 Rust +files in the `src` directory 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 @@ -59,8 +60,9 @@ 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. To run cargo-fmt, use +`cargo run --bin cargo-fmt` ## What style does Rustfmt use? From f5d1c067399dca9923089c29b7f26534370ec904 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Tue, 15 Dec 2015 23:57:05 +0100 Subject: [PATCH 5/8] Remove unneeded dependencies Remove dependency on Walkdir, which is no longer needed, because cargo-fmt now uses cargo read-manifest to get a list of targets to format. --- Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c2d7ecf5a476..70f7f796baf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ include = ["src/*.rs", "Cargo.toml"] [features] default = ["cargo-fmt"] -cargo-fmt = ["walkdir"] +cargo-fmt = [] [dependencies] toml = "0.1.20" @@ -25,5 +25,3 @@ syntex_syntax = "0.23.0" log = "0.3.2" env_logger = "0.3.1" getopts = "0.2" - -walkdir = {version = "0.1.5", optional = true} From 360f3efce8743d1eee6395881591cec52edb6934 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Wed, 16 Dec 2015 02:21:43 +0100 Subject: [PATCH 6/8] Replace `locate-project` with `read-manifest` Use `cargo read-manifest` instead of `cargo locate-project` to get a list of files to be formatted. --- Cargo.lock | 10 ----- src/bin/cargo-fmt.rs | 92 +++++++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73a16a224192..14afd0912485 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,6 @@ dependencies = [ "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -146,15 +145,6 @@ name = "unicode-xid" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "walkdir" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "winapi" version = "0.2.5" diff --git a/src/bin/cargo-fmt.rs b/src/bin/cargo-fmt.rs index 9c7d83705486..af6c6b059a05 100644 --- a/src/bin/cargo-fmt.rs +++ b/src/bin/cargo-fmt.rs @@ -14,7 +14,6 @@ #![cfg(feature="cargo-fmt")] extern crate getopts; -extern crate walkdir; extern crate rustc_serialize; use std::path::PathBuf; @@ -23,7 +22,6 @@ use std::str; use getopts::Options; -use walkdir::{WalkDir, DirEntry}; use rustc_serialize::json::Json; fn main() { @@ -47,45 +45,71 @@ fn main() { 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.", + println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt.", opts.usage(&msg)); } fn format_crate(opts: &Options) { - let mut root = match locate_root() { - Ok(r) => r, + let targets = match get_targets() { + Ok(t) => t, 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(); + // 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).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()); +#[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, std::io::Error> { + let mut targets: Vec = vec![]; + let output = try!(Command::new("cargo").arg("read-manifest").output()); if output.status.success() { - // We assume cargo locate-project is not broken and - // it will output a valid json document + // 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 root = PathBuf::from(json.find("root").unwrap().as_string().unwrap()); + let jtargets = json.find("targets").unwrap().as_array().unwrap(); + for jtarget in jtargets { + targets.push(target_from_json(jtarget)); + } - // root.parent() should never fail if locate-project's output is correct - Ok(root.parent().unwrap().to_owned()) + Ok(targets) } else { // This happens when cargo-fmt is not used inside a crate Err(std::io::Error::new(std::io::ErrorKind::NotFound, @@ -93,15 +117,19 @@ fn locate_root() -> Result { } } -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 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, } } From 959c2e7bed6e1fb95ac4252383d5ec0cdc401af3 Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Wed, 16 Dec 2015 04:41:58 +0100 Subject: [PATCH 7/8] Improve README.md --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7347499c824e..f8dab7ec97ff 100644 --- a/README.md +++ b/README.md @@ -21,15 +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. Alternatively, you can use `cargo fmt` to format all Rust -files in the `src` directory of your crate. +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 @@ -43,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 @@ -61,8 +68,7 @@ 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 --bin rustfmt -- filename`. See the -notes above on running rustfmt. To run cargo-fmt, use -`cargo run --bin cargo-fmt` +notes above on running rustfmt. ## What style does Rustfmt use? From 1e5e290e392adbf3e16f964bacb47a853cff3bdf Mon Sep 17 00:00:00 2001 From: Jan Likar Date: Wed, 16 Dec 2015 04:48:49 +0100 Subject: [PATCH 8/8] Allow to pass arguments to rustfmt --- src/bin/cargo-fmt.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/bin/cargo-fmt.rs b/src/bin/cargo-fmt.rs index af6c6b059a05..f79899ff5b1b 100644 --- a/src/bin/cargo-fmt.rs +++ b/src/bin/cargo-fmt.rs @@ -28,7 +28,7 @@ fn main() { let mut opts = getopts::Options::new(); opts.optflag("h", "help", "show this message"); - let matches = match opts.parse(env::args().skip(1)) { + let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) { Ok(m) => m, Err(e) => { print_usage(&opts, &e.to_string()); @@ -45,7 +45,8 @@ fn main() { 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.", + println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \ + Arguments after `--` are passes to rustfmt.", opts.usage(&msg)); } @@ -64,7 +65,15 @@ fn format_crate(opts: &Options) { .map(|t| t.path) .collect(); - format_files(&files).unwrap_or_else(|e| print_usage(opts, &e.to_string())); + format_files(&files, &get_fmt_args()).unwrap_or_else(|e| print_usage(opts, &e.to_string())); +} + +fn get_fmt_args() -> Vec { + 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)] @@ -133,10 +142,10 @@ fn target_from_json(jtarget: &Json) -> Target { } } -fn format_files(files: &Vec) -> Result<(), std::io::Error> { +fn format_files(files: &Vec, fmt_args: &Vec) -> Result<(), std::io::Error> { let mut command = try!(Command::new("rustfmt") - .arg("--write-mode=overwrite") .args(files) + .args(fmt_args) .spawn()); try!(command.wait());