]> git.lizzy.rs Git - rust.git/blob - src/config/config_type.rs
refactor: apply rustc mod parsing changes
[rust.git] / src / config / config_type.rs
1 use crate::config::file_lines::FileLines;
2 use crate::config::options::{IgnoreList, WidthHeuristics};
3
4 /// Trait for types that can be used in `Config`.
5 pub(crate) trait ConfigType: Sized {
6     /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
7     /// pipe-separated list of variants; for other types it returns "<type>".
8     fn doc_hint() -> String;
9 }
10
11 impl ConfigType for bool {
12     fn doc_hint() -> String {
13         String::from("<boolean>")
14     }
15 }
16
17 impl ConfigType for usize {
18     fn doc_hint() -> String {
19         String::from("<unsigned integer>")
20     }
21 }
22
23 impl ConfigType for isize {
24     fn doc_hint() -> String {
25         String::from("<signed integer>")
26     }
27 }
28
29 impl ConfigType for String {
30     fn doc_hint() -> String {
31         String::from("<string>")
32     }
33 }
34
35 impl ConfigType for FileLines {
36     fn doc_hint() -> String {
37         String::from("<json>")
38     }
39 }
40
41 impl ConfigType for WidthHeuristics {
42     fn doc_hint() -> String {
43         String::new()
44     }
45 }
46
47 impl ConfigType for IgnoreList {
48     fn doc_hint() -> String {
49         String::from("[<string>,..]")
50     }
51 }
52
53 macro_rules! create_config {
54     ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
55         #[cfg(test)]
56         use std::collections::HashSet;
57         use std::io::Write;
58
59         use serde::{Deserialize, Serialize};
60
61         #[derive(Clone)]
62         #[allow(unreachable_pub)]
63         pub struct Config {
64             // if a license_template_path has been specified, successfully read, parsed and compiled
65             // into a regex, it will be stored here
66             pub license_template: Option<Regex>,
67             // For each config item, we store a bool indicating whether it has
68             // been accessed and the value, and a bool whether the option was
69             // manually initialised, or taken from the default,
70             $($i: (Cell<bool>, bool, $ty, bool)),+
71         }
72
73         // Just like the Config struct but with each property wrapped
74         // as Option<T>. This is used to parse a rustfmt.toml that doesn't
75         // specify all properties of `Config`.
76         // We first parse into `PartialConfig`, then create a default `Config`
77         // and overwrite the properties with corresponding values from `PartialConfig`.
78         #[derive(Deserialize, Serialize, Clone)]
79         #[allow(unreachable_pub)]
80         pub struct PartialConfig {
81             $(pub $i: Option<$ty>),+
82         }
83
84         // Macro hygiene won't allow us to make `set_$i()` methods on Config
85         // for each item, so this struct is used to give the API to set values:
86         // `config.set().option(false)`. It's pretty ugly. Consider replacing
87         // with `config.set_option(false)` if we ever get a stable/usable
88         // `concat_idents!()`.
89         #[allow(unreachable_pub)]
90         pub struct ConfigSetter<'a>(&'a mut Config);
91
92         impl<'a> ConfigSetter<'a> {
93             $(
94             #[allow(unreachable_pub)]
95             pub fn $i(&mut self, value: $ty) {
96                 (self.0).$i.2 = value;
97                 match stringify!($i) {
98                     "max_width" | "use_small_heuristics" => self.0.set_heuristics(),
99                     "license_template_path" => self.0.set_license_template(),
100                     "merge_imports" => self.0.set_merge_imports(),
101                     &_ => (),
102                 }
103             }
104             )+
105         }
106
107         // Query each option, returns true if the user set the option, false if
108         // a default was used.
109         #[allow(unreachable_pub)]
110         pub struct ConfigWasSet<'a>(&'a Config);
111
112         impl<'a> ConfigWasSet<'a> {
113             $(
114             #[allow(unreachable_pub)]
115             pub fn $i(&self) -> bool {
116                 (self.0).$i.1
117             }
118             )+
119         }
120
121         impl Config {
122             $(
123             #[allow(unreachable_pub)]
124             pub fn $i(&self) -> $ty {
125                 self.$i.0.set(true);
126                 self.$i.2.clone()
127             }
128             )+
129
130             #[allow(unreachable_pub)]
131             pub fn set(&mut self) -> ConfigSetter<'_> {
132                 ConfigSetter(self)
133             }
134
135             #[allow(unreachable_pub)]
136             pub fn was_set(&self) -> ConfigWasSet<'_> {
137                 ConfigWasSet(self)
138             }
139
140             fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
141             $(
142                 if let Some(val) = parsed.$i {
143                     if self.$i.3 {
144                         self.$i.1 = true;
145                         self.$i.2 = val;
146                     } else {
147                         if crate::is_nightly_channel!() {
148                             self.$i.1 = true;
149                             self.$i.2 = val;
150                         } else {
151                             eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
152                                        available in nightly channel.", stringify!($i), val);
153                         }
154                     }
155                 }
156             )+
157                 self.set_heuristics();
158                 self.set_license_template();
159                 self.set_ignore(dir);
160                 self.set_merge_imports();
161                 self
162             }
163
164             /// Returns a hash set initialized with every user-facing config option name.
165             #[cfg(test)]
166             pub(crate) fn hash_set() -> HashSet<String> {
167                 let mut hash_set = HashSet::new();
168                 $(
169                     hash_set.insert(stringify!($i).to_owned());
170                 )+
171                 hash_set
172             }
173
174             pub(crate) fn is_valid_name(name: &str) -> bool {
175                 match name {
176                     $(
177                         stringify!($i) => true,
178                     )+
179                         _ => false,
180                 }
181             }
182
183             #[allow(unreachable_pub)]
184             pub fn is_valid_key_val(key: &str, val: &str) -> bool {
185                 match key {
186                     $(
187                         stringify!($i) => val.parse::<$ty>().is_ok(),
188                     )+
189                         _ => false,
190                 }
191             }
192
193             #[allow(unreachable_pub)]
194             pub fn used_options(&self) -> PartialConfig {
195                 PartialConfig {
196                     $(
197                         $i: if self.$i.0.get() {
198                                 Some(self.$i.2.clone())
199                             } else {
200                                 None
201                             },
202                     )+
203                 }
204             }
205
206             #[allow(unreachable_pub)]
207             pub fn all_options(&self) -> PartialConfig {
208                 PartialConfig {
209                     $(
210                         $i: Some(self.$i.2.clone()),
211                     )+
212                 }
213             }
214
215             #[allow(unreachable_pub)]
216             pub fn override_value(&mut self, key: &str, val: &str)
217             {
218                 match key {
219                     $(
220                         stringify!($i) => {
221                             self.$i.1 = true;
222                             self.$i.2 = val.parse::<$ty>()
223                                 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
224                                                  stringify!($i),
225                                                  val,
226                                                  stringify!($ty)));
227                         }
228                     )+
229                     _ => panic!("Unknown config key in override: {}", key)
230                 }
231
232                 match key {
233                     "max_width" | "use_small_heuristics" => self.set_heuristics(),
234                     "license_template_path" => self.set_license_template(),
235                     "merge_imports" => self.set_merge_imports(),
236                     &_ => (),
237                 }
238             }
239
240             #[allow(unreachable_pub)]
241             pub fn is_hidden_option(name: &str) -> bool {
242                 const HIDE_OPTIONS: [&str; 5] =
243                     ["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"];
244                 HIDE_OPTIONS.contains(&name)
245             }
246
247             #[allow(unreachable_pub)]
248             pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
249                 use std::cmp;
250                 let max = 0;
251                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
252                 let space_str = " ".repeat(max);
253                 writeln!(out, "Configuration Options:").unwrap();
254                 $(
255                     if $stb || include_unstable {
256                         let name_raw = stringify!($i);
257
258                         if !Config::is_hidden_option(name_raw) {
259                             let mut name_out = String::with_capacity(max);
260                             for _ in name_raw.len()..max-1 {
261                                 name_out.push(' ')
262                             }
263                             name_out.push_str(name_raw);
264                             name_out.push(' ');
265                             let mut default_str = format!("{}", $def);
266                             if default_str.is_empty() {
267                                 default_str = String::from("\"\"");
268                             }
269                             writeln!(out,
270                                     "{}{} Default: {}{}",
271                                     name_out,
272                                     <$ty>::doc_hint(),
273                                     default_str,
274                                     if !$stb { " (unstable)" } else { "" }).unwrap();
275                             $(
276                                 writeln!(out, "{}{}", space_str, $dstring).unwrap();
277                             )+
278                             writeln!(out).unwrap();
279                         }
280                     }
281                 )+
282             }
283
284             fn set_heuristics(&mut self) {
285                 if self.use_small_heuristics.2 == Heuristics::Default {
286                     let max_width = self.max_width.2;
287                     self.set().width_heuristics(WidthHeuristics::scaled(max_width));
288                 } else if self.use_small_heuristics.2 == Heuristics::Max {
289                     let max_width = self.max_width.2;
290                     self.set().width_heuristics(WidthHeuristics::set(max_width));
291                 } else {
292                     self.set().width_heuristics(WidthHeuristics::null());
293                 }
294             }
295
296             fn set_license_template(&mut self) {
297                 if self.was_set().license_template_path() {
298                     let lt_path = self.license_template_path();
299                     if lt_path.len() > 0 {
300                         match license::load_and_compile_template(&lt_path) {
301                             Ok(re) => self.license_template = Some(re),
302                             Err(msg) => eprintln!("Warning for license template file {:?}: {}",
303                                                 lt_path, msg),
304                         }
305                     } else {
306                         self.license_template = None;
307                     }
308                 }
309             }
310
311             fn set_ignore(&mut self, dir: &Path) {
312                 self.ignore.2.add_prefix(dir);
313             }
314
315             fn set_merge_imports(&mut self) {
316                 if self.was_set().merge_imports() {
317                     eprintln!(
318                         "Warning: the `merge_imports` option is deprecated. \
319                         Use `imports_granularity=Crate` instead"
320                     );
321                     if !self.was_set().imports_granularity() {
322                         self.imports_granularity.2 = if self.merge_imports() {
323                             ImportGranularity::Crate
324                         } else {
325                             ImportGranularity::Preserve
326                         };
327                     }
328                 }
329             }
330
331             #[allow(unreachable_pub)]
332             /// Returns `true` if the config key was explicitly set and is the default value.
333             pub fn is_default(&self, key: &str) -> bool {
334                 $(
335                     if let stringify!($i) = key {
336                         return self.$i.1 && self.$i.2 == $def;
337                     }
338                  )+
339                 false
340             }
341         }
342
343         // Template for the default configuration
344         impl Default for Config {
345             fn default() -> Config {
346                 Config {
347                     license_template: None,
348                     $(
349                         $i: (Cell::new(false), false, $def, $stb),
350                     )+
351                 }
352             }
353         }
354     )
355 }