diff --git a/README.md b/README.md index 03db8f375f2..61e01dcb9fd 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,18 @@ the rustfmt command. You can look at this repo for an example default.toml file. `cargo test` to run all tests. -`cargo run filename` to run on a file, if the file includes out of line modules, +`cargo run -- filename` to run on a 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 top file. You'll probably want to set the `WriteMode` in the call -to `run` in `main()`. Eventually you should be able to set the mode from the -command line or from a config file or something. +to run on the top file. +You'll probably want to specify the write mode. Currently, there are the replace, +overwrite and display mode. The replace mode is the default and overwrites the +original files after renaming them. In overwrite mode, rustfmt does not backup +the source files. To print the output to stdout, use the display mode. The write +mode can be set by passing the `--write-mode` flag on the command line. + +`cargo run -- filename --write-mode=display` prints the output of rustfmt to the +screen, for example. ## Use cases diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index a7f65b0bee0..6ec0f9556c8 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -9,23 +9,47 @@ // except according to those terms. #![cfg(not(test))] +#![feature(result_expect)] extern crate rustfmt; use rustfmt::{WriteMode, run}; +use rustfmt::config::Config; use std::fs::File; use std::io::Read; +use std::str::FromStr; fn main() { - let args: Vec<_> = std::env::args().collect(); let mut def_config_file = File::open("default.toml").unwrap_or_else(|e| { panic!("Unable to open configuration file [default.toml] {}",e) }); let mut def_config = String::new(); def_config_file.read_to_string(&mut def_config).unwrap(); + let config = Box::new(Config::from_toml(&def_config)); + let (args, write_mode) = determine_params(std::env::args()); - run(args, WriteMode::Overwrite, &def_config); + run(args, write_mode, config); std::process::exit(0); } + +fn determine_params(args: I) -> (Vec, WriteMode) + where I: Iterator +{ + let prefix = "--write-mode="; + let mut write_mode = WriteMode::Replace; + + // The NewFile option currently isn't supported because it requires another + // parameter, but it can be added later. + let args = args.filter(|arg| { + if arg.starts_with(prefix) { + write_mode = FromStr::from_str(&arg[prefix.len()..]).expect("Unrecognized write mode"); + false + } else { + true + } + }).collect(); + + (args, write_mode) +} diff --git a/src/filemap.rs b/src/filemap.rs index 8e09837df0a..ff50522eec6 100644 --- a/src/filemap.rs +++ b/src/filemap.rs @@ -73,40 +73,46 @@ fn write_file(text: &StringBuffer, } match mode { - WriteMode::Overwrite => { - // Do a little dance to make writing safer - write to a temp file - // rename the original to a .bk, then rename the temp file to the - // original. - let tmp_name = filename.to_owned() + ".tmp"; - let bk_name = filename.to_owned() + ".bk"; - { - // Write text to temp file - let tmp_file = try!(File::create(&tmp_name)); - try!(write_system_newlines(tmp_file, text, config)); - } + WriteMode::Replace => { + // Do a little dance to make writing safer - write to a temp file + // rename the original to a .bk, then rename the temp file to the + // original. + let tmp_name = filename.to_owned() + ".tmp"; + let bk_name = filename.to_owned() + ".bk"; + { + // Write text to temp file + let tmp_file = try!(File::create(&tmp_name)); + try!(write_system_newlines(tmp_file, text, config)); + } - try!(fs::rename(filename, bk_name)); - try!(fs::rename(tmp_name, filename)); - } - WriteMode::NewFile(extn) => { - let filename = filename.to_owned() + "." + extn; - let file = try!(File::create(&filename)); - try!(write_system_newlines(file, text, config)); - } - WriteMode::Display => { - println!("{}:\n", filename); - let stdout = stdout(); - let stdout_lock = stdout.lock(); - try!(write_system_newlines(stdout_lock, text, config)); - } - WriteMode::Return(_) => { - // io::Write is not implemented for String, working around with Vec - let mut v = Vec::new(); - try!(write_system_newlines(&mut v, text, config)); - // won't panic, we are writing correct utf8 - return Ok(Some(String::from_utf8(v).unwrap())); - } + try!(fs::rename(filename, bk_name)); + try!(fs::rename(tmp_name, filename)); } + WriteMode::Overwrite => { + // Write text directly over original file. + let file = try!(File::create(filename)); + try!(write_system_newlines(file, text, config)); + } + WriteMode::NewFile(extn) => { + let filename = filename.to_owned() + "." + extn; + let file = try!(File::create(&filename)); + try!(write_system_newlines(file, text, config)); + } + WriteMode::Display => { + println!("{}:\n", filename); + let stdout = stdout(); + let stdout_lock = stdout.lock(); + try!(write_system_newlines(stdout_lock, text, config)); + } + WriteMode::Return(_) => { + // io::Write is not implemented for String, working around with + // Vec + let mut v = Vec::new(); + try!(write_system_newlines(&mut v, text, config)); + // won't panic, we are writing correct utf8 + return Ok(Some(String::from_utf8(v).unwrap())); + } + } Ok(None) } diff --git a/src/lib.rs b/src/lib.rs index c0aff9a05b3..cc7111aae27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,7 @@ use std::path::PathBuf; use std::collections::HashMap; use std::fmt; use std::mem::swap; +use std::str::FromStr; use issues::{BadIssueSeeker, Issue}; use filemap::FileMap; @@ -71,8 +72,11 @@ const SKIP_ANNOTATION: &'static str = "rustfmt_skip"; #[derive(Copy, Clone)] pub enum WriteMode { + // Backups the original file and overwrites the orignal. + Replace, + // Overwrites original file without backup. Overwrite, - // str is the extension of the new file + // str is the extension of the new file. NewFile(&'static str), // Write the output to stdout. Display, @@ -80,6 +84,19 @@ pub enum WriteMode { Return(&'static Fn(HashMap)), } +impl FromStr for WriteMode { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "replace" => Ok(WriteMode::Replace), + "display" => Ok(WriteMode::Display), + "overwrite" => Ok(WriteMode::Overwrite), + _ => Err(()) + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum NewlineStyle { Windows, // \r\n @@ -343,9 +360,7 @@ impl<'a> CompilerCalls<'a> for RustFmtCalls { // to the compiler. // write_mode determines what happens to the result of running rustfmt, see // WriteMode. -// default_config is a string of toml data to be used to configure rustfmt. -pub fn run(args: Vec, write_mode: WriteMode, default_config: &str) { - let config = Some(Box::new(config::Config::from_toml(default_config))); - let mut call_ctxt = RustFmtCalls { write_mode: write_mode, config: config }; +pub fn run(args: Vec, write_mode: WriteMode, config: Box) { + let mut call_ctxt = RustFmtCalls { write_mode: write_mode, config: Some(config) }; rustc_driver::run_compiler(&args, &mut call_ctxt); } diff --git a/tests/system.rs b/tests/system.rs index 4b29376ffa7..19a68fc4b7b 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -19,6 +19,7 @@ use std::fs; use std::io::{self, Read, BufRead, BufReader}; use std::thread; use rustfmt::*; +use rustfmt::config::Config; fn get_path_string(dir_entry: io::Result) -> String { let path = dir_entry.ok().expect("Couldn't get DirEntry.").path(); @@ -103,14 +104,14 @@ pub fn idempotent_check(filename: String) -> Result<(), HashMap> // panic to return a result in case of failure. This has the advantage of smoothing the road to // multithreaded rustfmt thread::catch_panic(move || { - run(args, WriteMode::Return(HANDLE_RESULT), &config); + run(args, WriteMode::Return(HANDLE_RESULT), config); }).map_err(|any| *any.downcast().ok().expect("Downcast failed.") ) } // Reads test config file from comments and loads it -fn get_config(file_name: &str) -> String { +fn get_config(file_name: &str) -> Box { let config_file_name = read_significant_comment(file_name, "config") .map(|file_name| { let mut full_path = "tests/config/".to_owned(); @@ -123,7 +124,7 @@ fn get_config(file_name: &str) -> String { let mut def_config = String::new(); def_config_file.read_to_string(&mut def_config).ok().expect("Couldn't read config."); - def_config + Box::new(Config::from_toml(&def_config)) } fn read_significant_comment(file_name: &str, option: &str) -> Option {