use syntax::codemap::FileName;
use config::config_type::ConfigType;
use config::file_lines::FileLines;
use config::lists::*;
use config::Config;
use {FmtError, FmtResult, WRITE_MODE_LIST};
use getopts::Matches;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[macro_export]
macro_rules! impl_enum_serialize_and_deserialize {
( $e:ident, $( $x:ident ),* ) => {
impl ::serde::ser::Serialize for $e {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ::serde::ser::Serializer
{
use serde::ser::Error;
#[allow(unreachable_patterns)]
match *self {
$(
$e::$x => serializer.serialize_str(stringify!($x)),
)*
_ => {
Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
}
}
}
}
impl<'de> ::serde::de::Deserialize<'de> for $e {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where D: ::serde::Deserializer<'de> {
use serde::de::{Error, Visitor};
use std::marker::PhantomData;
use std::fmt;
struct StringOnly<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for StringOnly<T>
where T: ::serde::Deserializer<'de> {
type Value = String;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string")
}
fn visit_str<E>(self, value: &str) -> Result<String, E> {
Ok(String::from(value))
}
}
let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
$(
if stringify!($x).eq_ignore_ascii_case(&s) {
return Ok($e::$x);
}
)*
static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
Err(D::Error::unknown_variant(&s, ALLOWED))
}
}
impl ::std::str::FromStr for $e {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$(
if stringify!($x).eq_ignore_ascii_case(s) {
return Ok($e::$x);
}
)*
Err("Bad variant")
}
}
impl ConfigType for $e {
fn doc_hint() -> String {
let mut variants = Vec::new();
$(
variants.push(stringify!($x));
)*
format!("[{}]", variants.join("|"))
}
}
};
}
macro_rules! configuration_option_enum{
($e:ident: $( $x:ident ),+ $(,)*) => {
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum $e {
$( $x ),+
}
impl_enum_serialize_and_deserialize!($e, $( $x ),+);
}
}
configuration_option_enum! { NewlineStyle:
Windows, Unix, Native, }
configuration_option_enum! { BraceStyle:
AlwaysNextLine,
PreferSameLine,
SameLineWhere,
}
configuration_option_enum! { ControlBraceStyle:
AlwaysSameLine,
ClosingNextLine,
AlwaysNextLine,
}
configuration_option_enum! { IndentStyle:
Visual,
Block,
}
configuration_option_enum! { Density:
Compressed,
Tall,
Vertical,
}
configuration_option_enum! { TypeDensity:
Compressed,
Wide,
}
impl Density {
pub fn to_list_tactic(self) -> ListTactic {
match self {
Density::Compressed => ListTactic::Mixed,
Density::Tall => ListTactic::HorizontalVertical,
Density::Vertical => ListTactic::Vertical,
}
}
}
configuration_option_enum! { ReportTactic:
Always,
Unnumbered,
Never,
}
configuration_option_enum! { WriteMode:
Replace,
Overwrite,
Display,
Diff,
Coverage,
Plain,
Checkstyle,
Modified,
Check,
None,
}
configuration_option_enum! { Color:
Always,
Never,
Auto,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct WidthHeuristics {
pub fn_call_width: usize,
pub struct_lit_width: usize,
pub struct_variant_width: usize,
pub array_width: usize,
pub chain_width: usize,
pub single_line_if_else_max_width: usize,
}
impl WidthHeuristics {
pub fn null() -> WidthHeuristics {
WidthHeuristics {
fn_call_width: usize::max_value(),
struct_lit_width: 0,
struct_variant_width: 0,
array_width: usize::max_value(),
chain_width: usize::max_value(),
single_line_if_else_max_width: 0,
}
}
pub fn scaled(max_width: usize) -> WidthHeuristics {
let mut max_width_ratio: f32 = max_width as f32 / 100.0; max_width_ratio = (max_width_ratio * 10.0).round() / 10.0; WidthHeuristics {
fn_call_width: (60.0 * max_width_ratio).round() as usize,
struct_lit_width: (18.0 * max_width_ratio).round() as usize,
struct_variant_width: (35.0 * max_width_ratio).round() as usize,
array_width: (60.0 * max_width_ratio).round() as usize,
chain_width: (60.0 * max_width_ratio).round() as usize,
single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
}
}
}
impl ::std::str::FromStr for WidthHeuristics {
type Err = &'static str;
fn from_str(_: &str) -> Result<Self, Self::Err> {
Err("WidthHeuristics is not parsable")
}
}
impl Default for WriteMode {
fn default() -> WriteMode {
WriteMode::Overwrite
}
}
#[derive(Default, Deserialize, Serialize, Clone, Debug)]
pub struct IgnoreList(HashSet<PathBuf>);
impl IgnoreList {
pub fn add_prefix(&mut self, dir: &Path) {
self.0 = self.0
.iter()
.map(|s| {
if s.has_root() {
s.clone()
} else {
let mut path = PathBuf::from(dir);
path.push(s);
path
}
})
.collect();
}
fn skip_file_inner(&self, file: &Path) -> bool {
self.0.iter().any(|path| file.starts_with(path))
}
pub fn skip_file(&self, file: &FileName) -> bool {
if let FileName::Real(ref path) = file {
self.skip_file_inner(path)
} else {
false
}
}
}
impl ::std::str::FromStr for IgnoreList {
type Err = &'static str;
fn from_str(_: &str) -> Result<Self, Self::Err> {
Err("IgnoreList is not parsable")
}
}
#[derive(Clone, Debug, Default)]
pub struct CliOptions {
skip_children: Option<bool>,
verbose: bool,
verbose_diff: bool,
pub(super) config_path: Option<PathBuf>,
write_mode: Option<WriteMode>,
color: Option<Color>,
file_lines: FileLines, unstable_features: bool,
error_on_unformatted: Option<bool>,
}
impl CliOptions {
pub fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
let mut options = CliOptions::default();
options.verbose = matches.opt_present("verbose");
options.verbose_diff = matches.opt_present("verbose-diff");
let unstable_features = matches.opt_present("unstable-features");
let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
.map(|c| c == "nightly")
.unwrap_or(false);
if unstable_features && !rust_nightly {
return Err(FmtError::from(
"Unstable features are only available on Nightly channel",
));
} else {
options.unstable_features = unstable_features;
}
options.config_path = matches.opt_str("config-path").map(PathBuf::from);
if let Some(ref write_mode) = matches.opt_str("write-mode") {
if let Ok(write_mode) = WriteMode::from_str(write_mode) {
options.write_mode = Some(write_mode);
} else {
return Err(FmtError::from(format!(
"Invalid write-mode: {}, expected one of {}",
write_mode, WRITE_MODE_LIST
)));
}
}
if let Some(ref color) = matches.opt_str("color") {
match Color::from_str(color) {
Ok(color) => options.color = Some(color),
_ => return Err(FmtError::from(format!("Invalid color: {}", color))),
}
}
if let Some(ref file_lines) = matches.opt_str("file-lines") {
options.file_lines = file_lines.parse()?;
}
if matches.opt_present("skip-children") {
options.skip_children = Some(true);
}
if matches.opt_present("error-on-unformatted") {
options.error_on_unformatted = Some(true);
}
Ok(options)
}
pub fn apply_to(self, config: &mut Config) {
config.set().verbose(self.verbose);
config.set().verbose_diff(self.verbose_diff);
config.set().file_lines(self.file_lines);
config.set().unstable_features(self.unstable_features);
if let Some(skip_children) = self.skip_children {
config.set().skip_children(skip_children);
}
if let Some(error_on_unformatted) = self.error_on_unformatted {
config.set().error_on_unformatted(error_on_unformatted);
}
if let Some(write_mode) = self.write_mode {
config.set().write_mode(write_mode);
}
if let Some(color) = self.color {
config.set().color(color);
}
}
pub fn verify_file_lines(&self, files: &[PathBuf]) {
for f in self.file_lines.files() {
match *f {
FileName::Real(ref f) if files.contains(f) => {}
FileName::Real(_) => {
eprintln!("Warning: Extra file listed in file_lines option '{}'", f)
}
_ => eprintln!("Warning: Not a file '{}'", f),
}
}
}
}