Add option to override single configuration lines for tests

This commit is contained in:
Marcus Klaas 2015-08-19 21:41:19 +02:00
parent 2ef0b17955
commit 8e22a73cb7
14 changed files with 110 additions and 116 deletions

View file

@ -26,37 +26,56 @@ pub enum BlockIndentStyle {
impl_enum_decodable!(BlockIndentStyle, Inherit, Tabbed, Visual);
#[derive(RustcDecodable, Clone)]
pub struct Config {
pub max_width: usize,
pub ideal_width: usize,
pub leeway: usize,
pub tab_spaces: usize,
pub newline_style: NewlineStyle,
pub fn_brace_style: BraceStyle,
pub fn_return_indent: ReturnIndent,
pub fn_args_paren_newline: bool,
pub struct_trailing_comma: SeparatorTactic,
pub struct_lit_trailing_comma: SeparatorTactic,
pub struct_lit_style: StructLitStyle,
pub enum_trailing_comma: bool,
pub report_todo: ReportTactic,
pub report_fixme: ReportTactic,
pub reorder_imports: bool, // Alphabetically, case sensitive.
pub expr_indent_style: BlockIndentStyle,
}
macro_rules! create_config {
($($i:ident: $ty:ty),+ $(,)*) => (
#[derive(RustcDecodable, Clone)]
pub struct Config {
$(pub $i: $ty),+
}
impl Config {
pub fn from_toml(toml: &str) -> Config {
let parsed = toml.parse().unwrap();
match toml::decode(parsed) {
Some(decoded) => decoded,
None => {
println!("Decoding config file failed. Config:\n{}", toml);
let parsed: toml::Value = toml.parse().unwrap();
println!("\n\nParsed:\n{:?}", parsed);
panic!();
impl Config {
pub fn from_toml(toml: &str) -> Config {
let parsed = toml.parse().unwrap();
match toml::decode(parsed) {
Some(decoded) => decoded,
None => {
println!("Decoding config file failed. Config:\n{}", toml);
let parsed: toml::Value = toml.parse().unwrap();
println!("\n\nParsed:\n{:?}", parsed);
panic!();
}
}
}
pub fn override_value(&mut self, key: &str, val: &str) {
match key {
$(
stringify!($i) => {
self.$i = val.parse::<$ty>().unwrap();
}
)+
_ => panic!("Bad config key!")
}
}
}
}
)
}
create_config! {
max_width: usize,
ideal_width: usize,
leeway: usize,
tab_spaces: usize,
newline_style: NewlineStyle,
fn_brace_style: BraceStyle,
fn_return_indent: ReturnIndent,
fn_args_paren_newline: bool,
struct_trailing_comma: SeparatorTactic,
struct_lit_trailing_comma: SeparatorTactic,
struct_lit_style: StructLitStyle,
enum_trailing_comma: bool,
report_todo: ReportTactic,
report_fixme: ReportTactic,
reorder_imports: bool, // Alphabetically, case sensitive.
expr_indent_style: BlockIndentStyle,
}

View file

@ -617,10 +617,8 @@ fn rewrite_binary_op(context: &RewriteContext,
let operator_str = context.codemap.span_to_snippet(op.span).unwrap();
// 1 = space between lhs expr and operator
let mut result =
try_opt!(lhs.rewrite(context,
context.config.max_width - offset - 1 - operator_str.len(),
offset));
let max_width = try_opt!(context.config.max_width.checked_sub(operator_str.len() + offset + 1));
let mut result = try_opt!(lhs.rewrite(context, max_width, offset));
result.push(' ');
result.push_str(&operator_str);

View file

@ -80,7 +80,7 @@ pub enum WriteMode {
NewFile(&'static str),
// Write the output to stdout.
Display,
// Return the result as a mapping from filenames to StringBuffers.
// Return the result as a mapping from filenames to Strings.
Return(&'static Fn(HashMap<String, String>)),
}

View file

@ -144,6 +144,19 @@ macro_rules! impl_enum_decodable {
}
}
}
impl ::std::str::FromStr for $e {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &*s {
$(
stringify!($x) => Ok($e::$x),
)*
_ => Err("Bad variant"),
}
}
}
};
}

View file

@ -1,16 +0,0 @@
max_width = 100
ideal_width = 80
leeway = 5
tab_spaces = 4
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_style = "BlockIndent"
struct_lit_trailing_comma = "Vertical"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Visual"

View file

@ -1,16 +0,0 @@
max_width = 100
ideal_width = 80
leeway = 5
tab_spaces = 4
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_trailing_comma = "Vertical"
struct_lit_style = "BlockIndent"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = true
expr_indent_style = "Tabbed"

View file

@ -1,16 +0,0 @@
max_width = 100
ideal_width = 80
leeway = 5
tab_spaces = 4
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_style = "VisualIndent"
struct_lit_trailing_comma = "Vertical"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Tabbed"

View file

@ -1,4 +1,4 @@
// rustfmt-config: expr_visual_indent.toml
// rustfmt-expr_indent_style: Visual
// Visual level block indentation.

View file

@ -1,4 +1,4 @@
// rustfmt-config: reorder_imports.toml
// rustfmt-reorder_imports: true
use path::{C,/*A*/ A, B /* B */, self /* self */};

View file

@ -1,4 +1,4 @@
// rustfmt-config: visual_struct_lits.toml
// rustfmt-struct_lit_style: VisualIndent
// Struct literal expressions.

View file

@ -97,8 +97,16 @@ fn print_mismatches(result: HashMap<String, String>) {
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;
pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> {
let config = get_config(&filename);
let sig_comments = read_significant_comments(&filename);
let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));
let args = vec!["rustfmt".to_owned(), filename];
for (key, val) in sig_comments {
if key != "target" && key != "config" {
config.override_value(&key, &val);
}
}
// this thread is not used for concurrency, but rather to workaround the issue that the passed
// function handle needs to have static lifetime. Instead of using a global RefCell, we use
// panic to return a result in case of failure. This has the advantage of smoothing the road to
@ -110,15 +118,15 @@ pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>>
)
}
// Reads test config file from comments and loads it
fn get_config(file_name: &str) -> Box<Config> {
let config_file_name = read_significant_comment(file_name, "config")
.map(|file_name| {
let mut full_path = "tests/config/".to_owned();
full_path.push_str(&file_name);
full_path
})
.unwrap_or("default.toml".to_owned());
// Reads test config file from comments and reads its contents.
fn get_config(config_file: Option<&str>) -> Box<Config> {
let config_file_name = config_file.map(|file_name| {
let mut full_path = "tests/config/".to_owned();
full_path.push_str(&file_name);
full_path
})
.unwrap_or("default.toml".to_owned());
let mut def_config_file = fs::File::open(config_file_name).ok().expect("Couldn't open config.");
let mut def_config = String::new();
@ -127,14 +135,16 @@ fn get_config(file_name: &str) -> Box<Config> {
Box::new(Config::from_toml(&def_config))
}
fn read_significant_comment(file_name: &str, option: &str) -> Option<String> {
let file = fs::File::open(file_name).ok().expect("Couldn't read file for comment.");
// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
let file = fs::File::open(file_name).ok().expect(&format!("Couldn't read file {}.", file_name));
let reader = BufReader::new(file);
let pattern = format!("^\\s*//\\s*rustfmt-{}:\\s*(\\S+)", option);
let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");
// matches exactly the lines containing significant comments or whitespace
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[:alpha:]+:\s*\S+)")
// Matches lines containing significant comments or whitespace.
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
.ok().expect("Failed creating pattern 2.");
reader.lines()
@ -142,20 +152,26 @@ fn read_significant_comment(file_name: &str, option: &str) -> Option<String> {
.take_while(|line| line_regex.is_match(&line))
.filter_map(|line| {
regex.captures_iter(&line).next().map(|capture| {
capture.at(1).expect("Couldn't unwrap capture.").to_owned()
(capture.at(1).expect("Couldn't unwrap capture.").to_owned(),
capture.at(2).expect("Couldn't unwrap capture.").to_owned())
})
})
.next()
.collect()
}
// Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>) {
let mut failures = HashMap::new();
for (file_name, fmt_text) in result {
// If file is in tests/source, compare to file with same name in tests/target
let target_file_name = get_target(&file_name);
let mut f = fs::File::open(&target_file_name).ok().expect("Couldn't open target.");
// FIXME: reading significant comments again. Is there a way we can just
// pass the target to this function?
let sig_comments = read_significant_comments(&file_name);
// If file is in tests/source, compare to file with same name in tests/target.
let target = get_target(&file_name, sig_comments.get("target").map(|x| &(*x)[..]));
let mut f = fs::File::open(&target).ok().expect("Couldn't open target.");
let mut text = String::new();
// TODO: speedup by running through bytes iterator
@ -171,15 +187,11 @@ fn handle_result(result: HashMap<String, String>) {
}
// Map source file paths to their target paths.
fn get_target(file_name: &str) -> String {
fn get_target(file_name: &str, target: Option<&str>) -> String {
if file_name.starts_with("tests/source/") {
let target = read_significant_comment(file_name, "target");
let base = target.unwrap_or(file_name.trim_left_matches("tests/source/").to_owned());
let base = target.unwrap_or(file_name.trim_left_matches("tests/source/"));
let mut target_file = "tests/target/".to_owned();
target_file.push_str(&base);
target_file
format!("tests/target/{}", base)
} else {
file_name.to_owned()
}

View file

@ -1,4 +1,4 @@
// rustfmt-config: expr_visual_indent.toml
// rustfmt-expr_indent_style: Visual
// Visual level block indentation.

View file

@ -1,4 +1,4 @@
// rustfmt-config: reorder_imports.toml
// rustfmt-reorder_imports: true
use path::{self /* self */, /* A */ A, B /* B */, C};

View file

@ -1,4 +1,4 @@
// rustfmt-config: visual_struct_lits.toml
// rustfmt-struct_lit_style: VisualIndent
// Struct literal expressions.