extern crate toml;
+use std::{env, fs};
use std::cell::Cell;
-use std::fs;
use std::fs::File;
-use std::env;
use std::io::{Error, ErrorKind, Read};
use std::path::{Path, PathBuf};
use file_lines::FileLines;
-use lists::{SeparatorTactic, ListTactic};
+use lists::{ListTactic, SeparatorPlace, SeparatorTactic};
+use Summary;
macro_rules! configuration_option_enum{
($e:ident: $( $x:ident ),+ $(,)*) => {
pub fn to_list_tactic(self) -> ListTactic {
match self {
Density::Compressed => ListTactic::Mixed,
- Density::Tall |
- Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
+ Density::Tall | Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
Density::Vertical => ListTactic::Vertical,
}
}
#[derive(Clone)]
pub struct Config {
// For each config item, we store a bool indicating whether it has
- // been accessed and the value.
- $($i: (Cell<bool>, $ty)),+
+ // been accessed and the value, and a bool whether the option was
+ // manually initialised, or taken from the default,
+ $($i: (Cell<bool>, bool, $ty)),+
}
// Just like the Config struct but with each property wrapped
impl<'a> ConfigSetter<'a> {
$(
pub fn $i(&mut self, value: $ty) {
- (self.0).$i.1 = value;
+ (self.0).$i.2 = value;
+ }
+ )+
+ }
+
+ // Query each option, returns true if the user set the option, false if
+ // a default was used.
+ pub struct ConfigWasSet<'a>(&'a Config);
+
+ impl<'a> ConfigWasSet<'a> {
+ $(
+ pub fn $i(&self) -> bool {
+ (self.0).$i.1
}
)+
}
impl Config {
+ pub fn version_meets_requirement(&self, error_summary: &mut Summary) -> bool {
+ if self.was_set().required_version() {
+ let version = env!("CARGO_PKG_VERSION");
+ let required_version = self.required_version();
+ if version != required_version {
+ println!(
+ "Error: rustfmt version ({}) doesn't match the required version ({})",
+ version,
+ required_version,
+ );
+ error_summary.add_formatting_error();
+ return false;
+ }
+ }
+
+ true
+ }
$(
pub fn $i(&self) -> $ty {
self.$i.0.set(true);
- self.$i.1.clone()
+ self.$i.2.clone()
}
)+
ConfigSetter(self)
}
+ pub fn was_set<'a>(&'a self) -> ConfigWasSet<'a> {
+ ConfigWasSet(self)
+ }
+
fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
$(
if let Some(val) = parsed.$i {
- self.$i.1 = val;
+ self.$i.1 = true;
+ self.$i.2 = val;
}
)+
self
let table = parsed
.as_table()
.ok_or(String::from("Parsed config was not table"))?;
- for (key, _) in table {
+ for key in table.keys() {
match &**key {
$(
stringify!($i) => (),
PartialConfig {
$(
$i: if self.$i.0.get() {
- Some(self.$i.1.clone())
+ Some(self.$i.2.clone())
} else {
None
},
pub fn all_options(&self) -> PartialConfig {
PartialConfig {
$(
- $i: Some(self.$i.1.clone()),
+ $i: Some(self.$i.2.clone()),
)+
}
}
match key {
$(
stringify!($i) => {
- self.$i.1 = val.parse::<$ty>()
+ self.$i.2 = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
fn default() -> Config {
Config {
$(
- $i: (Cell::new(false), $def),
+ $i: (Cell::new(false), false, $def),
)+
}
}
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
- Err(e) => {
- if e.kind() != ErrorKind::NotFound {
- return Err(e);
- }
- }
+ Err(e) => if e.kind() != ErrorKind::NotFound {
+ return Err(e);
+ },
_ => {}
}
}
via the --file-lines option";
max_width: usize, 100, "Maximum width of each line";
error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
+ error_on_line_overflow_comments: bool, true, "Error if unable to get comments within max_width";
tab_spaces: usize, 4, "Number of spaces per tab";
fn_call_width: usize, 60,
"Maximum width of the args of a function call before falling back to vertical formatting";
newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
- control_style: Style, Style::Legacy, "Indent style for control flow statements";
+ control_style: Style, Style::Rfc, "Indent style for control flow statements";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
"Brace style for control flow constructs";
impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
"How to handle trailing commas for lists";
+ trailing_semicolon: bool, true, "Add trailing semicolon after break, continue and return";
fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
fn_single_line: bool, false, "Put single-expression functions on a single line";
fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
"Location of return type in function declaration";
- fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
+ fn_args_paren_newline: bool, false, "If function argument parenthesis goes on a newline";
fn_args_density: Density, Density::Tall, "Argument density in functions";
- fn_args_layout: IndentStyle, IndentStyle::Visual,
+ fn_args_layout: IndentStyle, IndentStyle::Block,
"Layout of function arguments and tuple structs";
- array_layout: IndentStyle, IndentStyle::Visual, "Indent on arrays";
+ array_layout: IndentStyle, IndentStyle::Block, "Indent on arrays";
array_width: usize, 60,
"Maximum width of an array literal before falling back to vertical formatting";
+ array_horizontal_layout_threshold: usize, 0,
+ "How many elements array must have before rustfmt uses horizontal layout.";
type_punctuation_density: TypeDensity, TypeDensity::Wide,
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
- where_style: Style, Style::Legacy, "Overall strategy for where clauses";
+ where_style: Style, Style::Rfc, "Overall strategy for where clauses";
// TODO:
// 1. Should we at least try to put the where clause on the same line as the rest of the
// function decl?
// 2. Currently options `Tall` and `Vertical` produce the same output.
- where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
+ where_density: Density, Density::Vertical, "Density of a where clause";
where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
where_pred_indent: IndentStyle, IndentStyle::Visual,
"Indentation style of a where predicate";
- generics_indent: IndentStyle, IndentStyle::Visual, "Indentation of generics";
+ generics_indent: IndentStyle, IndentStyle::Block, "Indentation of generics";
struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
"Multiline style on literal structs";
- fn_call_style: IndentStyle, IndentStyle::Visual, "Indentation for function calls, etc.";
+ fn_call_style: IndentStyle, IndentStyle::Block, "Indentation for function calls, etc.";
report_todo: ReportTactic, ReportTactic::Never,
"Report all, none or unnumbered occurrences of TODO in source file comments";
report_fixme: ReportTactic, ReportTactic::Never,
chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
chain_split_single_child: bool, false, "Split a chain with a single child if its length \
exceeds `chain_one_line_max`";
+ imports_indent: IndentStyle, IndentStyle::Visual, "Indent of imports";
+ imports_layout: ListTactic, ListTactic::Mixed, "Item layout inside a import block";
+ reorder_extern_crates: bool, true, "Reorder extern crate statements alphabetically";
+ reorder_extern_crates_in_group: bool, true, "Reorder extern crate statements in group";
reorder_imports: bool, false, "Reorder import statements alphabetically";
reorder_imports_in_group: bool, false, "Reorder import statements in group";
- reorder_imported_names: bool, false,
+ reorder_imported_names: bool, true,
"Reorder lists of names in import statements alphabetically";
single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
expressions. A value of zero means always break \
wrap_comments: bool, false, "Break comments to fit on the line";
comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
- wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
+ wrap_match_arms: bool, true, "Wrap the body of arms in blocks when it does not fit on \
+ the same line with the pattern of arms";
match_block_trailing_comma: bool, false,
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
+ match_arm_forces_newline: bool, false,
+ "Force match arm bodies to be in a new lines";
indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
indentation level as the match keyword";
+ match_pattern_separator_break_point: SeparatorPlace, SeparatorPlace::Back,
+ "Put a match sub-patterns' separator in front or back.";
closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
block indented. -1 means never use block indent.";
space_before_type_annotation: bool, false,
"Leave a space before the colon in a type annotation";
space_after_type_annotation_colon: bool, true,
"Leave a space after the colon in a type annotation";
+ space_before_struct_lit_field_colon: bool, false,
+ "Leave a space before the colon in a struct literal field";
+ space_after_struct_lit_field_colon: bool, true,
+ "Leave a space after the colon in a struct literal field";
space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
space_after_bound_colon: bool, true,
"Leave a space after the colon in a trait or lifetime bound";
spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
- write_mode: WriteMode, WriteMode::Replace,
- "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
+ write_mode: WriteMode, WriteMode::Overwrite,
+ "What Write Mode to use when none is supplied: \
+ Replace, Overwrite, Display, Plain, Diff, Coverage";
condense_wildcard_suffixes: bool, false, "Replace strings of _ wildcards by a single .. in \
tuple patterns";
- combine_control_expr: bool, false, "Combine control expressions with funciton calls."
+ combine_control_expr: bool, true, "Combine control expressions with function calls.";
+ struct_field_align_threshold: usize, 0, "Align struct fields if their diffs fits within \
+ threshold.";
+ remove_blank_lines_at_start_or_end_of_block: bool, true,
+ "Remove blank lines at start or end of a block";
+ attributes_on_same_line_as_field: bool, true,
+ "Try to put attributes on the same line as fields.";
+ attributes_on_same_line_as_variant: bool, true,
+ "Try to put attributes on the same line as variants in enum declarations.";
+ multiline_closure_forces_block: bool, false,
+ "Force multiline closure bodies to be wrapped in a block";
+ multiline_match_arm_forces_block: bool, false,
+ "Force multiline match arm bodies to be wrapped in a block";
+ merge_derives: bool, true, "Merge multiple `#[derive(...)]` into a single one";
+ binop_separator: SeparatorPlace, SeparatorPlace::Front,
+ "Where to put a binary operator when a binary expression goes multiline.";
+ required_version: String, env!("CARGO_PKG_VERSION").to_owned(),
+ "Require a specific version of rustfmt."
}
#[cfg(test)]
let used_options = config.used_options();
let toml = used_options.to_toml().unwrap();
- assert_eq!(toml,
- format!("verbose = {}\nskip_children = {}\n", verbose, skip_children));
+ assert_eq!(
+ toml,
+ format!("verbose = {}\nskip_children = {}\n", verbose, skip_children)
+ );
+ }
+
+ #[test]
+ fn test_was_set() {
+ let config = Config::from_toml("hard_tabs = true").unwrap();
+
+ assert_eq!(config.was_set().hard_tabs(), true);
+ assert_eq!(config.was_set().verbose(), false);
}
}