6 use std
::path
::{Path, PathBuf}
;
7 use std
::process
::Command
;
11 use getopts
::{Matches, Options}
;
12 use rustfmt_nightly
as rustfmt
;
14 use crate::rustfmt
::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}
;
16 fn prune_files(files
: Vec
<&str>) -> Vec
<&str> {
17 let prefixes
: Vec
<_
> = files
19 .filter(|f
| f
.ends_with("mod.rs") || f
.ends_with("lib.rs"))
20 .map(|f
| &f
[..f
.len() - 6])
23 let mut pruned_prefixes
= vec
![];
25 if p1
.starts_with("src/bin/") || pruned_prefixes
.iter().all(|p2
| !p1
.starts_with(p2
)) {
26 pruned_prefixes
.push(p1
);
29 debug
!("prefixes: {:?}", pruned_prefixes
);
34 if f
.ends_with("mod.rs") || f
.ends_with("lib.rs") || f
.starts_with("src/bin/") {
37 pruned_prefixes
.iter().all(|pp
| !f
.starts_with(pp
))
42 fn git_diff(commits
: &str) -> String
{
43 let mut cmd
= Command
::new("git");
46 cmd
.arg(format
!("HEAD~{}", commits
));
48 let output
= cmd
.output().expect("Couldn't execute `git diff`");
49 String
::from_utf8_lossy(&output
.stdout
).into_owned()
52 fn get_files(input
: &str) -> Vec
<&str> {
55 .filter(|line
| line
.starts_with("+++ b/") && line
.ends_with(".rs"))
56 .map(|line
| &line
[6..])
60 fn fmt_files(files
: &[&str]) -> i32 {
62 load_config
::<NullOptions
>(Some(Path
::new(".")), None
).expect("couldn't load config");
64 let mut exit_code
= 0;
65 let mut out
= stdout();
66 let mut session
= Session
::new(config
, Some(&mut out
));
68 let report
= session
.format(Input
::File(PathBuf
::from(file
))).unwrap();
69 if report
.has_warnings() {
70 eprintln
!("{}", FormatReportFormatterBuilder
::new(&report
).build());
72 if !session
.has_no_errors() {
81 impl CliOptions
for NullOptions
{
82 fn apply_to(self, _
: &mut rustfmt
::Config
) {
85 fn config_path(&self) -> Option
<&Path
> {
90 fn uncommitted_files() -> Vec
<String
> {
91 let mut cmd
= Command
::new("git");
94 cmd
.arg("--modified");
95 cmd
.arg("--exclude-standard");
96 let output
= cmd
.output().expect("Couldn't execute Git");
97 let stdout
= String
::from_utf8_lossy(&output
.stdout
);
100 .filter(|s
| s
.ends_with(".rs"))
101 .map(std
::borrow
::ToOwned
::to_owned
)
105 fn check_uncommitted() {
106 let uncommitted
= uncommitted_files();
107 debug
!("uncommitted files: {:?}", uncommitted
);
108 if !uncommitted
.is_empty() {
109 println
!("Found untracked changes:");
110 for f
in &uncommitted
{
113 println
!("Commit your work, or run with `-u`.");
114 println
!("Exiting.");
115 std
::process
::exit(1);
119 fn make_opts() -> Options
{
120 let mut opts
= Options
::new();
121 opts
.optflag("h", "help", "show this message");
122 opts
.optflag("c", "check", "check only, don't format (unimplemented)");
123 opts
.optflag("u", "uncommitted", "format uncommitted files");
134 fn from_args(matches
: &Matches
, opts
: &Options
) -> Config
{
135 // `--help` display help message and quit
136 if matches
.opt_present("h") {
137 let message
= format
!(
138 "\nusage: {} <commits> [options]\n\n\
139 commits: number of commits to format, default: 1",
140 env
::args_os().next().unwrap().to_string_lossy()
142 println
!("{}", opts
.usage(&message
));
143 std
::process
::exit(0);
146 let mut config
= Config
{
147 commits
: "1".to_owned(),
152 if matches
.opt_present("c") {
157 if matches
.opt_present("u") {
158 config
.uncommitted
= true;
161 if matches
.free
.len() > 1 {
162 panic
!("unknown arguments, use `-h` for usage");
164 if matches
.free
.len() == 1 {
165 let commits
= matches
.free
[0].trim();
166 if u32::from_str(commits
).is_err() {
167 panic
!("Couldn't parse number of commits");
169 config
.commits
= commits
.to_owned();
179 let opts
= make_opts();
181 .parse(env
::args().skip(1))
182 .expect("Couldn't parse command line");
183 let config
= Config
::from_args(&matches
, &opts
);
185 if !config
.uncommitted
{
189 let stdout
= git_diff(&config
.commits
);
190 let files
= get_files(&stdout
);
191 debug
!("files: {:?}", files
);
192 let files
= prune_files(files
);
193 debug
!("pruned files: {:?}", files
);
194 let exit_code
= fmt_files(&files
);
195 std
::process
::exit(exit_code
);