]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 { | |
f20569fa XL |
64 | // For each config item, we store a bool indicating whether it has |
65 | // been accessed and the value, and a bool whether the option was | |
66 | // manually initialised, or taken from the default, | |
67 | $($i: (Cell<bool>, bool, $ty, bool)),+ | |
68 | } | |
69 | ||
70 | // Just like the Config struct but with each property wrapped | |
71 | // as Option<T>. This is used to parse a rustfmt.toml that doesn't | |
72 | // specify all properties of `Config`. | |
73 | // We first parse into `PartialConfig`, then create a default `Config` | |
74 | // and overwrite the properties with corresponding values from `PartialConfig`. | |
75 | #[derive(Deserialize, Serialize, Clone)] | |
76 | #[allow(unreachable_pub)] | |
77 | pub struct PartialConfig { | |
78 | $(pub $i: Option<$ty>),+ | |
79 | } | |
80 | ||
81 | // Macro hygiene won't allow us to make `set_$i()` methods on Config | |
82 | // for each item, so this struct is used to give the API to set values: | |
83 | // `config.set().option(false)`. It's pretty ugly. Consider replacing | |
84 | // with `config.set_option(false)` if we ever get a stable/usable | |
85 | // `concat_idents!()`. | |
86 | #[allow(unreachable_pub)] | |
87 | pub struct ConfigSetter<'a>(&'a mut Config); | |
88 | ||
89 | impl<'a> ConfigSetter<'a> { | |
90 | $( | |
91 | #[allow(unreachable_pub)] | |
92 | pub fn $i(&mut self, value: $ty) { | |
93 | (self.0).$i.2 = value; | |
94 | match stringify!($i) { | |
cdc7bbd5 XL |
95 | "max_width" |
96 | | "use_small_heuristics" | |
97 | | "fn_call_width" | |
98 | | "single_line_if_else_max_width" | |
99 | | "attr_fn_like_width" | |
100 | | "struct_lit_width" | |
101 | | "struct_variant_width" | |
102 | | "array_width" | |
103 | | "chain_width" => self.0.set_heuristics(), | |
f20569fa XL |
104 | "merge_imports" => self.0.set_merge_imports(), |
105 | &_ => (), | |
106 | } | |
107 | } | |
108 | )+ | |
109 | } | |
110 | ||
111 | // Query each option, returns true if the user set the option, false if | |
112 | // a default was used. | |
113 | #[allow(unreachable_pub)] | |
114 | pub struct ConfigWasSet<'a>(&'a Config); | |
115 | ||
116 | impl<'a> ConfigWasSet<'a> { | |
117 | $( | |
118 | #[allow(unreachable_pub)] | |
119 | pub fn $i(&self) -> bool { | |
120 | (self.0).$i.1 | |
121 | } | |
122 | )+ | |
123 | } | |
124 | ||
125 | impl Config { | |
126 | $( | |
127 | #[allow(unreachable_pub)] | |
128 | pub fn $i(&self) -> $ty { | |
129 | self.$i.0.set(true); | |
130 | self.$i.2.clone() | |
131 | } | |
132 | )+ | |
133 | ||
134 | #[allow(unreachable_pub)] | |
135 | pub fn set(&mut self) -> ConfigSetter<'_> { | |
136 | ConfigSetter(self) | |
137 | } | |
138 | ||
139 | #[allow(unreachable_pub)] | |
140 | pub fn was_set(&self) -> ConfigWasSet<'_> { | |
141 | ConfigWasSet(self) | |
142 | } | |
143 | ||
144 | fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { | |
145 | $( | |
146 | if let Some(val) = parsed.$i { | |
147 | if self.$i.3 { | |
148 | self.$i.1 = true; | |
149 | self.$i.2 = val; | |
150 | } else { | |
151 | if crate::is_nightly_channel!() { | |
152 | self.$i.1 = true; | |
153 | self.$i.2 = val; | |
154 | } else { | |
155 | eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \ | |
156 | available in nightly channel.", stringify!($i), val); | |
157 | } | |
158 | } | |
159 | } | |
160 | )+ | |
161 | self.set_heuristics(); | |
f20569fa XL |
162 | self.set_ignore(dir); |
163 | self.set_merge_imports(); | |
164 | self | |
165 | } | |
166 | ||
167 | /// Returns a hash set initialized with every user-facing config option name. | |
168 | #[cfg(test)] | |
169 | pub(crate) fn hash_set() -> HashSet<String> { | |
170 | let mut hash_set = HashSet::new(); | |
171 | $( | |
172 | hash_set.insert(stringify!($i).to_owned()); | |
173 | )+ | |
174 | hash_set | |
175 | } | |
176 | ||
177 | pub(crate) fn is_valid_name(name: &str) -> bool { | |
178 | match name { | |
179 | $( | |
180 | stringify!($i) => true, | |
181 | )+ | |
182 | _ => false, | |
183 | } | |
184 | } | |
185 | ||
186 | #[allow(unreachable_pub)] | |
187 | pub fn is_valid_key_val(key: &str, val: &str) -> bool { | |
188 | match key { | |
189 | $( | |
190 | stringify!($i) => val.parse::<$ty>().is_ok(), | |
191 | )+ | |
192 | _ => false, | |
193 | } | |
194 | } | |
195 | ||
196 | #[allow(unreachable_pub)] | |
197 | pub fn used_options(&self) -> PartialConfig { | |
198 | PartialConfig { | |
199 | $( | |
200 | $i: if self.$i.0.get() { | |
201 | Some(self.$i.2.clone()) | |
202 | } else { | |
203 | None | |
204 | }, | |
205 | )+ | |
206 | } | |
207 | } | |
208 | ||
209 | #[allow(unreachable_pub)] | |
210 | pub fn all_options(&self) -> PartialConfig { | |
211 | PartialConfig { | |
212 | $( | |
213 | $i: Some(self.$i.2.clone()), | |
214 | )+ | |
215 | } | |
216 | } | |
217 | ||
218 | #[allow(unreachable_pub)] | |
219 | pub fn override_value(&mut self, key: &str, val: &str) | |
220 | { | |
221 | match key { | |
222 | $( | |
223 | stringify!($i) => { | |
224 | self.$i.1 = true; | |
225 | self.$i.2 = val.parse::<$ty>() | |
226 | .expect(&format!("Failed to parse override for {} (\"{}\") as a {}", | |
227 | stringify!($i), | |
228 | val, | |
229 | stringify!($ty))); | |
230 | } | |
231 | )+ | |
232 | _ => panic!("Unknown config key in override: {}", key) | |
233 | } | |
234 | ||
235 | match key { | |
cdc7bbd5 XL |
236 | "max_width" |
237 | | "use_small_heuristics" | |
238 | | "fn_call_width" | |
239 | | "single_line_if_else_max_width" | |
240 | | "attr_fn_like_width" | |
241 | | "struct_lit_width" | |
242 | | "struct_variant_width" | |
243 | | "array_width" | |
244 | | "chain_width" => self.set_heuristics(), | |
f20569fa XL |
245 | "merge_imports" => self.set_merge_imports(), |
246 | &_ => (), | |
247 | } | |
248 | } | |
249 | ||
250 | #[allow(unreachable_pub)] | |
251 | pub fn is_hidden_option(name: &str) -> bool { | |
252 | const HIDE_OPTIONS: [&str; 5] = | |
253 | ["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"]; | |
254 | HIDE_OPTIONS.contains(&name) | |
255 | } | |
256 | ||
257 | #[allow(unreachable_pub)] | |
258 | pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { | |
259 | use std::cmp; | |
260 | let max = 0; | |
261 | $( let max = cmp::max(max, stringify!($i).len()+1); )+ | |
262 | let space_str = " ".repeat(max); | |
263 | writeln!(out, "Configuration Options:").unwrap(); | |
264 | $( | |
265 | if $stb || include_unstable { | |
266 | let name_raw = stringify!($i); | |
267 | ||
268 | if !Config::is_hidden_option(name_raw) { | |
269 | let mut name_out = String::with_capacity(max); | |
270 | for _ in name_raw.len()..max-1 { | |
271 | name_out.push(' ') | |
272 | } | |
273 | name_out.push_str(name_raw); | |
274 | name_out.push(' '); | |
275 | let mut default_str = format!("{}", $def); | |
276 | if default_str.is_empty() { | |
277 | default_str = String::from("\"\""); | |
278 | } | |
279 | writeln!(out, | |
280 | "{}{} Default: {}{}", | |
281 | name_out, | |
282 | <$ty>::doc_hint(), | |
283 | default_str, | |
284 | if !$stb { " (unstable)" } else { "" }).unwrap(); | |
285 | $( | |
286 | writeln!(out, "{}{}", space_str, $dstring).unwrap(); | |
287 | )+ | |
288 | writeln!(out).unwrap(); | |
289 | } | |
290 | } | |
291 | )+ | |
292 | } | |
293 | ||
cdc7bbd5 XL |
294 | fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) { |
295 | let max_width = self.max_width.2; | |
296 | let get_width_value = | | |
297 | was_set: bool, | |
298 | override_value: usize, | |
299 | heuristic_value: usize, | |
300 | config_key: &str, | |
301 | | -> usize { | |
302 | if !was_set { | |
303 | return heuristic_value; | |
304 | } | |
305 | if override_value > max_width { | |
306 | eprintln!( | |
307 | "`{0}` cannot have a value that exceeds `max_width`. \ | |
308 | `{0}` will be set to the same value as `max_width`", | |
309 | config_key, | |
310 | ); | |
311 | return max_width; | |
312 | } | |
313 | override_value | |
314 | }; | |
315 | ||
316 | let fn_call_width = get_width_value( | |
317 | self.was_set().fn_call_width(), | |
318 | self.fn_call_width.2, | |
319 | heuristics.fn_call_width, | |
320 | "fn_call_width", | |
321 | ); | |
322 | self.fn_call_width.2 = fn_call_width; | |
323 | ||
324 | let attr_fn_like_width = get_width_value( | |
325 | self.was_set().attr_fn_like_width(), | |
326 | self.attr_fn_like_width.2, | |
327 | heuristics.attr_fn_like_width, | |
328 | "attr_fn_like_width", | |
329 | ); | |
330 | self.attr_fn_like_width.2 = attr_fn_like_width; | |
331 | ||
332 | let struct_lit_width = get_width_value( | |
333 | self.was_set().struct_lit_width(), | |
334 | self.struct_lit_width.2, | |
335 | heuristics.struct_lit_width, | |
336 | "struct_lit_width", | |
337 | ); | |
338 | self.struct_lit_width.2 = struct_lit_width; | |
339 | ||
340 | let struct_variant_width = get_width_value( | |
341 | self.was_set().struct_variant_width(), | |
342 | self.struct_variant_width.2, | |
343 | heuristics.struct_variant_width, | |
344 | "struct_variant_width", | |
345 | ); | |
346 | self.struct_variant_width.2 = struct_variant_width; | |
347 | ||
348 | let array_width = get_width_value( | |
349 | self.was_set().array_width(), | |
350 | self.array_width.2, | |
351 | heuristics.array_width, | |
352 | "array_width", | |
353 | ); | |
354 | self.array_width.2 = array_width; | |
355 | ||
356 | let chain_width = get_width_value( | |
357 | self.was_set().chain_width(), | |
358 | self.chain_width.2, | |
359 | heuristics.chain_width, | |
360 | "chain_width", | |
361 | ); | |
362 | self.chain_width.2 = chain_width; | |
363 | ||
364 | let single_line_if_else_max_width = get_width_value( | |
365 | self.was_set().single_line_if_else_max_width(), | |
366 | self.single_line_if_else_max_width.2, | |
367 | heuristics.single_line_if_else_max_width, | |
368 | "single_line_if_else_max_width", | |
369 | ); | |
370 | self.single_line_if_else_max_width.2 = single_line_if_else_max_width; | |
371 | } | |
372 | ||
f20569fa | 373 | fn set_heuristics(&mut self) { |
cdc7bbd5 XL |
374 | let max_width = self.max_width.2; |
375 | match self.use_small_heuristics.2 { | |
376 | Heuristics::Default => | |
377 | self.set_width_heuristics(WidthHeuristics::scaled(max_width)), | |
378 | Heuristics::Max => self.set_width_heuristics(WidthHeuristics::set(max_width)), | |
379 | Heuristics::Off => self.set_width_heuristics(WidthHeuristics::null()), | |
380 | }; | |
f20569fa XL |
381 | } |
382 | ||
f20569fa XL |
383 | fn set_ignore(&mut self, dir: &Path) { |
384 | self.ignore.2.add_prefix(dir); | |
385 | } | |
386 | ||
387 | fn set_merge_imports(&mut self) { | |
388 | if self.was_set().merge_imports() { | |
389 | eprintln!( | |
390 | "Warning: the `merge_imports` option is deprecated. \ | |
94222f64 | 391 | Use `imports_granularity=\"Crate\"` instead" |
f20569fa XL |
392 | ); |
393 | if !self.was_set().imports_granularity() { | |
394 | self.imports_granularity.2 = if self.merge_imports() { | |
395 | ImportGranularity::Crate | |
396 | } else { | |
397 | ImportGranularity::Preserve | |
398 | }; | |
399 | } | |
400 | } | |
401 | } | |
402 | ||
403 | #[allow(unreachable_pub)] | |
404 | /// Returns `true` if the config key was explicitly set and is the default value. | |
405 | pub fn is_default(&self, key: &str) -> bool { | |
406 | $( | |
407 | if let stringify!($i) = key { | |
408 | return self.$i.1 && self.$i.2 == $def; | |
409 | } | |
410 | )+ | |
411 | false | |
412 | } | |
413 | } | |
414 | ||
415 | // Template for the default configuration | |
416 | impl Default for Config { | |
417 | fn default() -> Config { | |
418 | Config { | |
f20569fa XL |
419 | $( |
420 | $i: (Cell::new(false), false, $def, $stb), | |
421 | )+ | |
422 | } | |
423 | } | |
424 | } | |
425 | ) | |
426 | } |