use config::config_type::ConfigType;
use config::lists::*;
use config::{Config, FileName};
use isatty::stdout_isatty;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
#[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:
Auto, Windows, Unix, Native, }
impl NewlineStyle {
fn auto_detect(raw_input_text: &str) -> NewlineStyle {
if let Some(pos) = raw_input_text.find('\n') {
let pos = pos.saturating_sub(1);
if let Some('\r') = raw_input_text.chars().nth(pos) {
NewlineStyle::Windows
} else {
NewlineStyle::Unix
}
} else {
NewlineStyle::Native
}
}
fn native() -> NewlineStyle {
if cfg!(windows) {
NewlineStyle::Windows
} else {
NewlineStyle::Unix
}
}
pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
use NewlineStyle::*;
let mut style = self;
if style == Auto {
style = Self::auto_detect(raw_input_text);
}
if style == Native {
style = Self::native();
}
match style {
Windows => {
let mut transformed = String::with_capacity(2 * formatted_text.capacity());
for c in formatted_text.chars() {
match c {
'\n' => transformed.push_str("\r\n"),
'\r' => continue,
c => transformed.push(c),
}
}
*formatted_text = transformed;
}
Unix => return,
Native => unreachable!("NewlineStyle::Native"),
Auto => unreachable!("NewlineStyle::Auto"),
}
}
}
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,
}
configuration_option_enum! { Heuristics:
Off,
Max,
Default,
}
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! { EmitMode:
Files,
Stdout,
Coverage,
Checkstyle,
ModifiedLines,
Diff,
}
configuration_option_enum! { Color:
Always,
Never,
Auto,
}
impl Color {
pub fn use_colored_tty(&self) -> bool {
match self {
Color::Always => true,
Color::Never => false,
Color::Auto => stdout_isatty(),
}
}
}
configuration_option_enum! { Verbosity:
Verbose,
Normal,
Quiet,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
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 set(max_width: usize) -> WidthHeuristics {
WidthHeuristics {
fn_call_width: max_width,
struct_lit_width: max_width,
struct_variant_width: max_width,
array_width: max_width,
chain_width: max_width,
single_line_if_else_max_width: max_width,
}
}
pub fn scaled(max_width: usize) -> WidthHeuristics {
const DEFAULT_MAX_WIDTH: usize = 100;
let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
(ratio * 10.0).round() / 10.0
} else {
1.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 EmitMode {
fn default() -> EmitMode {
EmitMode::Files
}
}
#[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)]
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")
}
}
pub trait CliOptions {
fn apply_to(self, config: &mut Config);
fn config_path(&self) -> Option<&Path>;
}
configuration_option_enum!{ Edition:
Edition2015,
Edition2018,
}
impl Edition {
pub(crate) fn to_libsyntax_pos_edition(&self) -> syntax_pos::edition::Edition {
match self {
Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
}
}
}
#[test]
fn test_newline_style_auto_detect() {
let lf = "One\nTwo\nThree";
let crlf = "One\r\nTwo\r\nThree";
let none = "One Two Three";
assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
}
#[test]
fn test_newline_style_auto_apply() {
let auto = NewlineStyle::Auto;
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\nTwo\nThree";
let mut out = String::from(formatted_text);
auto.apply(&mut out, raw_input_text);
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\r\nTwo\r\nThree";
let mut out = String::from(formatted_text);
auto.apply(&mut out, raw_input_text);
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
#[cfg(not(windows))]
{
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One Two Three";
let mut out = String::from(formatted_text);
auto.apply(&mut out, raw_input_text);
assert_eq!(
"One\nTwo\nThree", &out,
"auto-native-unix should detect 'lf'"
);
}
#[cfg(windows)]
{
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One Two Three";
let mut out = String::from(formatted_text);
auto.apply(&mut out, raw_input_text);
assert_eq!(
"One\r\nTwo\r\nThree", &out,
"auto-native-windows should detect 'crlf'"
);
}
}