diff options
| author | Nick Cameron <ncameron@mozilla.com> | 2018-03-02 14:28:34 +1300 |
|---|---|---|
| committer | Nick Cameron <ncameron@mozilla.com> | 2018-03-02 14:58:23 +1300 |
| commit | 39301ae5f21b6d6c8155d3b46ec6bf21f92de864 (patch) | |
| tree | 4602a0e55e869bcf55df5ca31b4bf329e874c7c4 /src/git-rustfmt | |
| parent | 6154f2b3e58e18345389a6343ac083a8947b397a (diff) | |
Go back to a non-workspace structure
Kinda reverts https://github.com/rust-lang-nursery/rustfmt/pull/2419
Diffstat (limited to 'src/git-rustfmt')
| -rw-r--r-- | src/git-rustfmt/main.rs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs new file mode 100644 index 00000000000..8811c1a5255 --- /dev/null +++ b/src/git-rustfmt/main.rs @@ -0,0 +1,205 @@ +// Copyright 2018 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. + +extern crate env_logger; +extern crate getopts; +#[macro_use] +extern crate log; +extern crate rustfmt_nightly as rustfmt; + +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str::FromStr; + +use getopts::{Matches, Options}; + +use rustfmt::{config, run, Input}; + +fn prune_files(files: Vec<&str>) -> Vec<&str> { + let prefixes: Vec<_> = files + .iter() + .filter(|f| f.ends_with("mod.rs") || f.ends_with("lib.rs")) + .map(|f| &f[..f.len() - 6]) + .collect(); + + let mut pruned_prefixes = vec![]; + for p1 in prefixes { + let mut include = true; + if !p1.starts_with("src/bin/") { + for p2 in &pruned_prefixes { + if p1.starts_with(p2) { + include = false; + break; + } + } + } + if include { + pruned_prefixes.push(p1); + } + } + debug!("prefixes: {:?}", pruned_prefixes); + + files + .into_iter() + .filter(|f| { + let mut include = true; + if f.ends_with("mod.rs") || f.ends_with("lib.rs") || f.starts_with("src/bin/") { + return true; + } + for pp in &pruned_prefixes { + if f.starts_with(pp) { + include = false; + break; + } + } + include + }) + .collect() +} + +fn git_diff(commits: &str) -> String { + let mut cmd = Command::new("git"); + cmd.arg("diff"); + if commits != "0" { + cmd.arg(format!("HEAD~{}", commits)); + } + let output = cmd.output().expect("Couldn't execute `git diff`"); + String::from_utf8_lossy(&output.stdout).into_owned() +} + +fn get_files(input: &str) -> Vec<&str> { + input + .lines() + .filter(|line| line.starts_with("+++ b/") && line.ends_with(".rs")) + .map(|line| &line[6..]) + .collect() +} + +fn fmt_files(files: &[&str]) -> i32 { + let (config, _) = config::Config::from_resolved_toml_path(Path::new(".")) + .unwrap_or_else(|_| (config::Config::default(), None)); + + let mut exit_code = 0; + for file in files { + let summary = run(Input::File(PathBuf::from(file)), &config); + if !summary.has_no_errors() { + exit_code = 1; + } + } + exit_code +} + +fn uncommitted_files() -> Vec<String> { + let mut cmd = Command::new("git"); + cmd.arg("ls-files"); + cmd.arg("--others"); + cmd.arg("--modified"); + cmd.arg("--exclude-standard"); + let output = cmd.output().expect("Couldn't execute Git"); + let stdout = String::from_utf8_lossy(&output.stdout); + stdout + .lines() + .filter(|s| s.ends_with(".rs")) + .map(|s| s.to_owned()) + .collect() +} + +fn check_uncommitted() { + let uncommitted = uncommitted_files(); + debug!("uncommitted files: {:?}", uncommitted); + if !uncommitted.is_empty() { + println!("Found untracked changes:"); + for f in &uncommitted { + println!(" {}", f); + } + println!("Commit your work, or run with `-u`."); + println!("Exiting."); + std::process::exit(1); + } +} + +fn make_opts() -> Options { + let mut opts = Options::new(); + opts.optflag("h", "help", "show this message"); + opts.optflag("c", "check", "check only, don't format (unimplemented)"); + opts.optflag("u", "uncommitted", "format uncommitted files"); + opts +} + +struct Config { + commits: String, + uncommitted: bool, + check: bool, +} + +impl Config { + fn from_args(matches: &Matches, opts: &Options) -> Config { + // `--help` display help message and quit + if matches.opt_present("h") { + let message = format!( + "\nusage: {} <commits> [options]\n\n\ + commits: number of commits to format, default: 1", + env::args_os().next().unwrap().to_string_lossy() + ); + println!("{}", opts.usage(&message)); + std::process::exit(0); + } + + let mut config = Config { + commits: "1".to_owned(), + uncommitted: false, + check: false, + }; + + if matches.opt_present("c") { + config.check = true; + unimplemented!(); + } + + if matches.opt_present("u") { + config.uncommitted = true; + } + + if matches.free.len() > 1 { + panic!("unknown arguments, use `-h` for usage"); + } + if matches.free.len() == 1 { + let commits = matches.free[0].trim(); + if u32::from_str(commits).is_err() { + panic!("Couldn't parse number of commits"); + } + config.commits = commits.to_owned(); + } + + config + } +} + +fn main() { + let _ = env_logger::init(); + + let opts = make_opts(); + let matches = opts.parse(env::args().skip(1)) + .expect("Couldn't parse command line"); + let config = Config::from_args(&matches, &opts); + + if !config.uncommitted { + check_uncommitted(); + } + + let stdout = git_diff(&config.commits); + let files = get_files(&stdout); + debug!("files: {:?}", files); + let files = prune_files(files); + debug!("pruned files: {:?}", files); + let exit_code = fmt_files(&files); + std::process::exit(exit_code); +} |
