1
1
//! Read configurations files.
2
2
3
- #![ deny ( clippy:: missing_docs_in_private_items ) ]
3
+ #![ allow ( clippy:: module_name_repetitions ) ]
4
4
5
- use std:: lazy:: SyncLazy ;
5
+ use serde:: de:: { Deserializer , IgnoredAny , IntoDeserializer , MapAccess , Visitor } ;
6
+ use serde:: Deserialize ;
7
+ use std:: error:: Error ;
6
8
use std:: path:: { Path , PathBuf } ;
7
- use std:: sync:: Mutex ;
8
9
use std:: { env, fmt, fs, io} ;
9
10
10
- /// Error from reading a configuration file.
11
- #[ derive( Debug ) ]
12
- pub enum Error {
13
- /// An I/O error.
14
- Io ( io:: Error ) ,
15
- /// Not valid toml or doesn't fit the expected config format
16
- Toml ( String ) ,
11
+ /// Conf with parse errors
12
+ #[ derive( Default ) ]
13
+ pub struct TryConf {
14
+ pub conf : Conf ,
15
+ pub errors : Vec < String > ,
17
16
}
18
17
19
- impl fmt :: Display for Error {
20
- fn fmt ( & self , f : & mut fmt :: Formatter < ' _ > ) -> fmt :: Result {
21
- match self {
22
- Self :: Io ( err ) => err . fmt ( f ) ,
23
- Self :: Toml ( err ) => err . fmt ( f ) ,
18
+ impl TryConf {
19
+ fn from_error ( error : impl Error ) -> Self {
20
+ Self {
21
+ conf : Conf :: default ( ) ,
22
+ errors : vec ! [ error . to_string ( ) ] ,
24
23
}
25
24
}
26
25
}
27
26
28
- impl From < io:: Error > for Error {
29
- fn from ( e : io:: Error ) -> Self {
30
- Self :: Io ( e)
31
- }
32
- }
27
+ macro_rules! define_Conf {
28
+ ( $(
29
+ #[ $doc: meta]
30
+ $( #[ conf_deprecated( $dep: literal) ] ) ?
31
+ ( $name: ident: $ty: ty $( = $default: expr) ?) ,
32
+ ) * ) => {
33
+ /// Clippy lint configuration
34
+ pub struct Conf {
35
+ $( #[ $doc] pub $name: $ty, ) *
36
+ }
33
37
34
- /// Vec of errors that might be collected during config toml parsing
35
- static ERRORS : SyncLazy < Mutex < Vec < Error > > > = SyncLazy :: new ( || Mutex :: new ( Vec :: new ( ) ) ) ;
38
+ mod defaults {
39
+ $( pub fn $name( ) -> $ty { define_Conf!( @default $( $default) ?) } ) *
40
+ }
36
41
37
- macro_rules! define_Conf {
38
- ( $( #[ $doc: meta] ( $config: ident, $config_str: literal: $Ty: ty, $default: expr) , ) +) => {
39
- mod helpers {
40
- use serde:: Deserialize ;
41
- /// Type used to store lint configuration.
42
- #[ derive( Deserialize ) ]
43
- #[ serde( rename_all = "kebab-case" , deny_unknown_fields) ]
44
- pub struct Conf {
45
- $(
46
- #[ $doc]
47
- #[ serde( default = $config_str) ]
48
- #[ serde( with = $config_str) ]
49
- pub $config: $Ty,
50
- ) +
51
- #[ allow( dead_code) ]
52
- #[ serde( default ) ]
53
- third_party: Option <:: toml:: Value >,
42
+ impl Default for Conf {
43
+ fn default ( ) -> Self {
44
+ Self { $( $name: defaults:: $name( ) , ) * }
54
45
}
46
+ }
55
47
56
- $(
57
- mod $config {
58
- use serde:: Deserialize ;
59
- pub fn deserialize<' de, D : serde:: Deserializer <' de>>( deserializer: D ) -> Result <$Ty, D :: Error > {
60
- use super :: super :: { ERRORS , Error } ;
61
-
62
- Ok (
63
- <$Ty>:: deserialize( deserializer) . unwrap_or_else( |e| {
64
- ERRORS
65
- . lock( )
66
- . expect( "no threading here" )
67
- . push( Error :: Toml ( e. to_string( ) ) ) ;
68
- super :: $config( )
69
- } )
70
- )
71
- }
72
- }
48
+ impl <' de> Deserialize <' de> for TryConf {
49
+ fn deserialize<D >( deserializer: D ) -> Result <Self , D :: Error > where D : Deserializer <' de> {
50
+ deserializer. deserialize_map( ConfVisitor )
51
+ }
52
+ }
53
+
54
+ #[ derive( Deserialize ) ]
55
+ #[ serde( field_identifier, rename_all = "kebab-case" ) ]
56
+ #[ allow( non_camel_case_types) ]
57
+ enum Field { $( $name, ) * third_party, }
58
+
59
+ struct ConfVisitor ;
60
+
61
+ impl <' de> Visitor <' de> for ConfVisitor {
62
+ type Value = TryConf ;
63
+
64
+ fn expecting( & self , formatter: & mut fmt:: Formatter <' _>) -> fmt:: Result {
65
+ formatter. write_str( "Conf" )
66
+ }
73
67
74
- #[ must_use]
75
- fn $config( ) -> $Ty {
76
- let x = $default;
77
- x
68
+ fn visit_map<V >( self , mut map: V ) -> Result <Self :: Value , V :: Error > where V : MapAccess <' de> {
69
+ let mut errors = Vec :: new( ) ;
70
+ $( let mut $name = None ; ) *
71
+ // could get `Field` here directly, but get `str` first for diagnostics
72
+ while let Some ( name) = map. next_key:: <& str >( ) ? {
73
+ match Field :: deserialize( name. into_deserializer( ) ) ? {
74
+ $( Field :: $name => {
75
+ $( errors. push( format!( "deprecated field `{}`. {}" , name, $dep) ) ; ) ?
76
+ match map. next_value( ) {
77
+ Err ( e) => errors. push( e. to_string( ) ) ,
78
+ Ok ( value) => match $name {
79
+ Some ( _) => errors. push( format!( "duplicate field `{}`" , name) ) ,
80
+ None => $name = Some ( value) ,
81
+ }
82
+ }
83
+ } ) *
84
+ // white-listed; ignore
85
+ Field :: third_party => drop( map. next_value:: <IgnoredAny >( ) )
86
+ }
78
87
}
79
- ) +
88
+ let conf = Conf { $( $name: $name. unwrap_or_else( defaults:: $name) , ) * } ;
89
+ Ok ( TryConf { conf, errors } )
90
+ }
80
91
}
81
92
} ;
93
+ ( @default ) => ( Default :: default ( ) ) ;
94
+ ( @default $default: expr) => ( $default) ;
82
95
}
83
96
84
- pub use self :: helpers:: Conf ;
85
97
define_Conf ! {
86
98
/// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports
87
- ( msrv, "msrv" : Option <String >, None ) ,
99
+ ( msrv: Option <String >) ,
88
100
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
89
- ( blacklisted_names, "blacklisted_names" : Vec <String >, [ "foo" , "baz" , "quux" ] . iter( ) . map( ToString :: to_string) . collect( ) ) ,
101
+ ( blacklisted_names: Vec <String > = [ "foo" , "baz" , "quux" ] . iter( ) . map( ToString :: to_string) . collect( ) ) ,
90
102
/// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
91
- ( cognitive_complexity_threshold, "cognitive_complexity_threshold" : u64 , 25 ) ,
103
+ ( cognitive_complexity_threshold: u64 = 25 ) ,
92
104
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
93
- ( cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold" : Option <u64 >, None ) ,
105
+ #[ conf_deprecated( "Please use `cognitive-complexity-threshold` instead." ) ]
106
+ ( cyclomatic_complexity_threshold: Option <u64 >) ,
94
107
/// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
95
- ( doc_valid_idents, "doc_valid_idents" : Vec <String >, [
108
+ ( doc_valid_idents: Vec <String > = [
96
109
"KiB" , "MiB" , "GiB" , "TiB" , "PiB" , "EiB" ,
97
110
"DirectX" ,
98
111
"ECMAScript" ,
@@ -113,54 +126,47 @@ define_Conf! {
113
126
"CamelCase" ,
114
127
] . iter( ) . map( ToString :: to_string) . collect( ) ) ,
115
128
/// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
116
- ( too_many_arguments_threshold, "too_many_arguments_threshold" : u64 , 7 ) ,
129
+ ( too_many_arguments_threshold: u64 = 7 ) ,
117
130
/// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
118
- ( type_complexity_threshold, "type_complexity_threshold" : u64 , 250 ) ,
131
+ ( type_complexity_threshold: u64 = 250 ) ,
119
132
/// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
120
- ( single_char_binding_names_threshold, "single_char_binding_names_threshold" : u64 , 4 ) ,
133
+ ( single_char_binding_names_threshold: u64 = 4 ) ,
121
134
/// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
122
- ( too_large_for_stack, "too_large_for_stack" : u64 , 200 ) ,
135
+ ( too_large_for_stack: u64 = 200 ) ,
123
136
/// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
124
- ( enum_variant_name_threshold, "enum_variant_name_threshold" : u64 , 3 ) ,
137
+ ( enum_variant_name_threshold: u64 = 3 ) ,
125
138
/// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
126
- ( enum_variant_size_threshold, "enum_variant_size_threshold" : u64 , 200 ) ,
139
+ ( enum_variant_size_threshold: u64 = 200 ) ,
127
140
/// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
128
- ( verbose_bit_mask_threshold, "verbose_bit_mask_threshold" : u64 , 1 ) ,
141
+ ( verbose_bit_mask_threshold: u64 = 1 ) ,
129
142
/// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
130
- ( literal_representation_threshold, "literal_representation_threshold" : u64 , 16384 ) ,
143
+ ( literal_representation_threshold: u64 = 16384 ) ,
131
144
/// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
132
- ( trivial_copy_size_limit, "trivial_copy_size_limit" : Option <u64 >, None ) ,
145
+ ( trivial_copy_size_limit: Option <u64 >) ,
133
146
/// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
134
- ( pass_by_value_size_limit, "pass_by_value_size_limit" : u64 , 256 ) ,
147
+ ( pass_by_value_size_limit: u64 = 256 ) ,
135
148
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
136
- ( too_many_lines_threshold, "too_many_lines_threshold" : u64 , 100 ) ,
149
+ ( too_many_lines_threshold: u64 = 100 ) ,
137
150
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
138
- ( array_size_threshold, "array_size_threshold" : u64 , 512_000 ) ,
151
+ ( array_size_threshold: u64 = 512_000 ) ,
139
152
/// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
140
- ( vec_box_size_threshold, "vec_box_size_threshold" : u64 , 4096 ) ,
153
+ ( vec_box_size_threshold: u64 = 4096 ) ,
141
154
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
142
- ( max_trait_bounds, "max_trait_bounds" : u64 , 3 ) ,
155
+ ( max_trait_bounds: u64 = 3 ) ,
143
156
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
144
- ( max_struct_bools, "max_struct_bools" : u64 , 3 ) ,
157
+ ( max_struct_bools: u64 = 3 ) ,
145
158
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
146
- ( max_fn_params_bools, "max_fn_params_bools" : u64 , 3 ) ,
159
+ ( max_fn_params_bools: u64 = 3 ) ,
147
160
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
148
- ( warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports" : bool , false ) ,
161
+ ( warn_on_all_wildcard_imports: bool ) ,
149
162
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
150
- ( disallowed_methods, "disallowed_methods" : Vec <String >, Vec :: < String > :: new ( ) ) ,
163
+ ( disallowed_methods: Vec <String >) ,
151
164
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
152
- ( unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions" : bool , true ) ,
165
+ ( unreadable_literal_lint_fractions: bool = true ) ,
153
166
/// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
154
- ( upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive" : bool , false ) ,
167
+ ( upper_case_acronyms_aggressive: bool ) ,
155
168
/// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
156
- ( cargo_ignore_publish, "cargo_ignore_publish" : bool , false ) ,
157
- }
158
-
159
- impl Default for Conf {
160
- #[ must_use]
161
- fn default ( ) -> Self {
162
- toml:: from_str ( "" ) . expect ( "we never error on empty config files" )
163
- }
169
+ ( cargo_ignore_publish: bool ) ,
164
170
}
165
171
166
172
/// Search for the configuration file.
@@ -194,43 +200,13 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
194
200
}
195
201
}
196
202
197
- /// Produces a `Conf` filled with the default values and forwards the errors
198
- ///
199
- /// Used internally for convenience
200
- fn default ( errors : Vec < Error > ) -> ( Conf , Vec < Error > ) {
201
- ( Conf :: default ( ) , errors)
202
- }
203
-
204
203
/// Read the `toml` configuration file.
205
204
///
206
205
/// In case of error, the function tries to continue as much as possible.
207
- pub fn read ( path : & Path ) -> ( Conf , Vec < Error > ) {
206
+ pub fn read ( path : & Path ) -> TryConf {
208
207
let content = match fs:: read_to_string ( path) {
208
+ Err ( e) => return TryConf :: from_error ( e) ,
209
209
Ok ( content) => content,
210
- Err ( err) => return default ( vec ! [ err. into( ) ] ) ,
211
210
} ;
212
-
213
- assert ! ( ERRORS . lock( ) . expect( "no threading -> mutex always safe" ) . is_empty( ) ) ;
214
- match toml:: from_str ( & content) {
215
- Ok ( toml) => {
216
- let mut errors = ERRORS . lock ( ) . expect ( "no threading -> mutex always safe" ) . split_off ( 0 ) ;
217
-
218
- let toml_ref: & Conf = & toml;
219
-
220
- let cyc_field: Option < u64 > = toml_ref. cyclomatic_complexity_threshold ;
221
-
222
- if cyc_field. is_some ( ) {
223
- let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead." . to_string ( ) ;
224
- errors. push ( Error :: Toml ( cyc_err) ) ;
225
- }
226
-
227
- ( toml, errors)
228
- } ,
229
- Err ( e) => {
230
- let mut errors = ERRORS . lock ( ) . expect ( "no threading -> mutex always safe" ) . split_off ( 0 ) ;
231
- errors. push ( Error :: Toml ( e. to_string ( ) ) ) ;
232
-
233
- default ( errors)
234
- } ,
235
- }
211
+ toml:: from_str ( & content) . unwrap_or_else ( TryConf :: from_error)
236
212
}
0 commit comments