use std::io::Read;
use syntax::{ast, codemap};
use toml;
+use std::sync::Mutex;
/// Get the configuration file from arguments.
pub fn file_from_args(args: &[codemap::Spanned<ast::NestedMetaItemKind>])
pub enum Error {
/// An I/O error.
Io(io::Error),
- /// The file is not valid TOML.
- Toml(Vec<toml::ParserError>),
+ /// Not valid toml or doesn't fit the expected conf format
+ Toml(String),
/// Type error.
Type(/// The name of the key.
&'static str,
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Error::Io(ref err) => err.fmt(f),
- Error::Toml(ref errs) => {
- let mut first = true;
- for err in errs {
- if !first {
- try!(", ".fmt(f));
- first = false;
- }
-
- try!(err.fmt(f));
- }
-
- Ok(())
- },
+ Error::Toml(ref err) => err.fmt(f),
Error::Type(key, expected, got) => {
write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
},
}
}
-macro_rules! define_Conf {
- ($(#[$doc: meta] ($toml_name: tt, $rust_name: ident, $default: expr => $($ty: tt)+),)+) => {
- /// Type used to store lint configuration.
- pub struct Conf {
- $(#[$doc] pub $rust_name: define_Conf!(TY $($ty)+),)+
- }
+lazy_static! {
+ static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
+}
- impl Default for Conf {
- fn default() -> Conf {
- Conf {
- $($rust_name: define_Conf!(DEFAULT $($ty)+, $default),)+
- }
+macro_rules! define_Conf {
+ ($(#[$doc: meta] ($rust_name: ident, $rust_name_str: expr, $default: expr => $($ty: tt)+),)+) => {
+ pub use self::helpers::Conf;
+ mod helpers {
+ /// Type used to store lint configuration.
+ #[derive(Deserialize)]
+ #[serde(rename_all="kebab-case")]
+ #[serde(deny_unknown_fields)]
+ pub struct Conf {
+ $(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)] pub $rust_name: define_Conf!(TY $($ty)+),)+
+ #[allow(dead_code)]
+ #[serde(default)]
+ third_party: Option<::toml::Value>,
}
- }
-
- impl Conf {
- /// Set the property `name` (which must be the `toml` name) to the given value
- #[allow(cast_sign_loss)]
- fn set(&mut self, name: String, value: toml::Value) -> Result<(), Error> {
- match name.as_str() {
- $(
- define_Conf!(PAT $toml_name) => {
- if let Some(value) = define_Conf!(CONV $($ty)+, value) {
- self.$rust_name = value;
- }
- else {
- return Err(Error::Type(define_Conf!(EXPR $toml_name),
- stringify!($($ty)+),
- value.type_str()));
- }
- },
- )+
- "third-party" => {
- // for external tools such as clippy-service
- return Ok(());
- }
- _ => {
- return Err(Error::UnknownKey(name));
+ $(
+ mod $rust_name {
+ use serde;
+ use serde::Deserialize;
+ pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<define_Conf!(TY $($ty)+), D::Error> {
+ type T = define_Conf!(TY $($ty)+);
+ Ok(T::deserialize(deserializer).unwrap_or_else(|e| {
+ ::utils::conf::ERRORS.lock().expect("no threading here").push(::utils::conf::Error::Toml(e.to_string()));
+ super::$rust_name()
+ }))
}
}
- Ok(())
- }
+ fn $rust_name() -> define_Conf!(TY $($ty)+) {
+ define_Conf!(DEFAULT $($ty)+, $default)
+ }
+ )+
}
};
// hack to convert tts
- (PAT $pat: pat) => { $pat };
- (EXPR $e: expr) => { $e };
(TY $ty: ty) => { $ty };
// how to read the value?
};
(CONV String, $value: expr) => { $value.as_str().map(Into::into) };
(CONV Vec<String>, $value: expr) => {{
- let slice = $value.as_slice();
+ let slice = $value.as_array();
if let Some(slice) = slice {
if slice.iter().any(|v| v.as_str().is_none()) {
define_Conf! {
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
- ("blacklisted-names", blacklisted_names, ["foo", "bar", "baz", "quux"] => Vec<String>),
+ (blacklisted_names, "blacklisted_names", ["foo", "bar", "baz", "quux"] => Vec<String>),
/// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
- ("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
+ (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold", 25 => u64),
/// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
- ("doc-valid-idents", doc_valid_idents, [
+ (doc_valid_idents, "doc_valid_idents", [
"KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
"DirectX",
"ECMAScript",
"MinGW",
] => Vec<String>),
/// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
- ("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
+ (too_many_arguments_threshold, "too_many_arguments_threshold", 7 => u64),
/// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
- ("type-complexity-threshold", type_complexity_threshold, 250 => u64),
+ (type_complexity_threshold, "type_complexity_threshold", 250 => u64),
/// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
- ("single-char-binding-names-threshold", max_single_char_names, 5 => u64),
+ (single_char_binding_names_threshold, "single_char_binding_names_threshold", 5 => u64),
/// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
- ("too-large-for-stack", too_large_for_stack, 200 => u64),
+ (too_large_for_stack, "too_large_for_stack", 200 => u64),
/// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
- ("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
+ (enum_variant_name_threshold, "enum_variant_name_threshold", 3 => u64),
/// Lint: LARGE_ENUM_VARIANT. The maximum size of a emum's variant to avoid box suggestion
- ("enum-variant-size-threshold", enum_variant_size_threshold, 200 => u64),
+ (enum_variant_size_threshold, "enum_variant_size_threshold", 200 => u64),
}
/// Search for the configuration file.
}
}
+fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
+ (toml::from_str("").expect("we never error on empty config files"), errors)
+}
+
/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
- let mut conf = Conf::default();
- let mut errors = Vec::new();
-
let path = if let Some(path) = path {
path
} else {
- return (conf, errors);
+ return default(Vec::new())
};
let file = match fs::File::open(path) {
let mut buf = String::new();
if let Err(err) = file.read_to_string(&mut buf) {
- errors.push(err.into());
- return (conf, errors);
+ return default(vec![err.into()])
}
buf
},
- Err(err) => {
- errors.push(err.into());
- return (conf, errors);
- },
+ Err(err) => return default(vec![err.into()]),
};
- let mut parser = toml::Parser::new(&file);
- let toml = if let Some(toml) = parser.parse() {
- toml
- } else {
- errors.push(Error::Toml(parser.errors));
- return (conf, errors);
- };
-
- for (key, value) in toml {
- if let Err(err) = conf.set(key, value) {
- errors.push(err);
- }
+ assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
+ match toml::from_str(&file) {
+ Ok(toml) => (toml, ERRORS.lock().expect("no threading -> mutex always safe").split_off(0)),
+ Err(e) => {
+ let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
+ errors.push(Error::Toml(e.to_string()));
+ default(errors)
+ },
}
-
- (conf, errors)
}