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); impl_enum_decodable!(BlockIndentStyle, Inherit, Tabbed, Visual);
#[derive(RustcDecodable, Clone)] macro_rules! create_config {
pub struct Config { ($($i:ident: $ty:ty),+ $(,)*) => (
pub max_width: usize, #[derive(RustcDecodable, Clone)]
pub ideal_width: usize, pub struct Config {
pub leeway: usize, $(pub $i: $ty),+
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,
}
impl Config { impl Config {
pub fn from_toml(toml: &str) -> Config { pub fn from_toml(toml: &str) -> Config {
let parsed = toml.parse().unwrap(); let parsed = toml.parse().unwrap();
match toml::decode(parsed) { match toml::decode(parsed) {
Some(decoded) => decoded, Some(decoded) => decoded,
None => { None => {
println!("Decoding config file failed. Config:\n{}", toml); println!("Decoding config file failed. Config:\n{}", toml);
let parsed: toml::Value = toml.parse().unwrap(); let parsed: toml::Value = toml.parse().unwrap();
println!("\n\nParsed:\n{:?}", parsed); println!("\n\nParsed:\n{:?}", parsed);
panic!(); 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(); let operator_str = context.codemap.span_to_snippet(op.span).unwrap();
// 1 = space between lhs expr and operator // 1 = space between lhs expr and operator
let mut result = let max_width = try_opt!(context.config.max_width.checked_sub(operator_str.len() + offset + 1));
try_opt!(lhs.rewrite(context, let mut result = try_opt!(lhs.rewrite(context, max_width, offset));
context.config.max_width - offset - 1 - operator_str.len(),
offset));
result.push(' '); result.push(' ');
result.push_str(&operator_str); result.push_str(&operator_str);

View file

@ -80,7 +80,7 @@ pub enum WriteMode {
NewFile(&'static str), NewFile(&'static str),
// Write the output to stdout. // Write the output to stdout.
Display, 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>)), 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. // 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 */}; 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. // 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; static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;
pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> { 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]; 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 // 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 // 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 // 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> { // Reads test config file from comments and reads its contents.
let config_file_name = read_significant_comment(file_name, "config") fn get_config(config_file: Option<&str>) -> Box<Config> {
.map(|file_name| { let config_file_name = config_file.map(|file_name| {
let mut full_path = "tests/config/".to_owned(); let mut full_path = "tests/config/".to_owned();
full_path.push_str(&file_name); full_path.push_str(&file_name);
full_path full_path
}) })
.unwrap_or("default.toml".to_owned()); .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_file = fs::File::open(config_file_name).ok().expect("Couldn't open config.");
let mut def_config = String::new(); 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)) Box::new(Config::from_toml(&def_config))
} }
fn read_significant_comment(file_name: &str, option: &str) -> Option<String> { // Reads significant comments of the form: // rustfmt-key: value
let file = fs::File::open(file_name).ok().expect("Couldn't read file for comment."); // 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 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."); let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");
// matches exactly the lines containing significant comments or whitespace // Matches lines containing significant comments or whitespace.
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[:alpha:]+:\s*\S+)") let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
.ok().expect("Failed creating pattern 2."); .ok().expect("Failed creating pattern 2.");
reader.lines() 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)) .take_while(|line| line_regex.is_match(&line))
.filter_map(|line| { .filter_map(|line| {
regex.captures_iter(&line).next().map(|capture| { 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. // Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>) { fn handle_result(result: HashMap<String, String>) {
let mut failures = HashMap::new(); let mut failures = HashMap::new();
for (file_name, fmt_text) in result { for (file_name, fmt_text) in result {
// If file is in tests/source, compare to file with same name in tests/target // FIXME: reading significant comments again. Is there a way we can just
let target_file_name = get_target(&file_name); // pass the target to this function?
let mut f = fs::File::open(&target_file_name).ok().expect("Couldn't open target."); 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(); let mut text = String::new();
// TODO: speedup by running through bytes iterator // 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. // 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/") { 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/"));
let base = target.unwrap_or(file_name.trim_left_matches("tests/source/").to_owned());
let mut target_file = "tests/target/".to_owned(); format!("tests/target/{}", base)
target_file.push_str(&base);
target_file
} else { } else {
file_name.to_owned() 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. // 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}; 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. // Struct literal expressions.