1 use crate::config::file_lines::FileLines;
2 use crate::config::options::{IgnoreList, WidthHeuristics};
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;
11 impl ConfigType for bool {
12 fn doc_hint() -> String {
13 String::from("<boolean>")
17 impl ConfigType for usize {
18 fn doc_hint() -> String {
19 String::from("<unsigned integer>")
23 impl ConfigType for isize {
24 fn doc_hint() -> String {
25 String::from("<signed integer>")
29 impl ConfigType for String {
30 fn doc_hint() -> String {
31 String::from("<string>")
35 impl ConfigType for FileLines {
36 fn doc_hint() -> String {
37 String::from("<json>")
41 impl ConfigType for WidthHeuristics {
42 fn doc_hint() -> String {
47 impl ConfigType for IgnoreList {
48 fn doc_hint() -> String {
49 String::from("[<string>,..]")
53 /// Checks if we're in a nightly build.
55 /// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap
56 /// to "stable", "beta", or "nightly" depending on what toolchain is being built.
57 /// If we are being built as part of the stable or beta toolchains, we want
58 /// to disable unstable configuration options.
60 /// If we're being built by cargo (e.g., `cargo +nightly install rustfmt-nightly`),
61 /// `CFG_RELEASE_CHANNEL` is not set. As we only support being built against the
62 /// nightly compiler when installed from crates.io, default to nightly mode.
63 macro_rules! is_nightly_channel {
65 option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev")
69 macro_rules! create_config {
70 ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
72 use std::collections::HashSet;
75 use serde::{Deserialize, Serialize};
78 #[allow(unreachable_pub)]
80 // if a license_template_path has been specified, successfully read, parsed and compiled
81 // into a regex, it will be stored here
82 pub license_template: Option<Regex>,
83 // For each config item, we store a bool indicating whether it has
84 // been accessed and the value, and a bool whether the option was
85 // manually initialised, or taken from the default,
86 $($i: (Cell<bool>, bool, $ty, bool)),+
89 // Just like the Config struct but with each property wrapped
90 // as Option<T>. This is used to parse a rustfmt.toml that doesn't
91 // specify all properties of `Config`.
92 // We first parse into `PartialConfig`, then create a default `Config`
93 // and overwrite the properties with corresponding values from `PartialConfig`.
94 #[derive(Deserialize, Serialize, Clone)]
95 #[allow(unreachable_pub)]
96 pub struct PartialConfig {
97 $(pub $i: Option<$ty>),+
100 // Macro hygiene won't allow us to make `set_$i()` methods on Config
101 // for each item, so this struct is used to give the API to set values:
102 // `config.set().option(false)`. It's pretty ugly. Consider replacing
103 // with `config.set_option(false)` if we ever get a stable/usable
104 // `concat_idents!()`.
105 #[allow(unreachable_pub)]
106 pub struct ConfigSetter<'a>(&'a mut Config);
108 impl<'a> ConfigSetter<'a> {
110 #[allow(unreachable_pub)]
111 pub fn $i(&mut self, value: $ty) {
112 (self.0).$i.2 = value;
113 match stringify!($i) {
114 "max_width" | "use_small_heuristics" => self.0.set_heuristics(),
115 "license_template_path" => self.0.set_license_template(),
122 // Query each option, returns true if the user set the option, false if
123 // a default was used.
124 #[allow(unreachable_pub)]
125 pub struct ConfigWasSet<'a>(&'a Config);
127 impl<'a> ConfigWasSet<'a> {
129 #[allow(unreachable_pub)]
130 pub fn $i(&self) -> bool {
138 #[allow(unreachable_pub)]
139 pub fn $i(&self) -> $ty {
145 #[allow(unreachable_pub)]
146 pub fn set(&mut self) -> ConfigSetter<'_> {
150 #[allow(unreachable_pub)]
151 pub fn was_set(&self) -> ConfigWasSet<'_> {
155 fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
157 if let Some(val) = parsed.$i {
162 if is_nightly_channel!() {
166 eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
167 available in nightly channel.", stringify!($i), val);
172 self.set_heuristics();
173 self.set_license_template();
174 self.set_ignore(dir);
178 /// Returns a hash set initialized with every user-facing config option name.
180 pub(crate) fn hash_set() -> HashSet<String> {
181 let mut hash_set = HashSet::new();
183 hash_set.insert(stringify!($i).to_owned());
188 pub(crate) fn is_valid_name(name: &str) -> bool {
191 stringify!($i) => true,
197 #[allow(unreachable_pub)]
198 pub fn used_options(&self) -> PartialConfig {
201 $i: if self.$i.0.get() {
202 Some(self.$i.2.clone())
210 #[allow(unreachable_pub)]
211 pub fn all_options(&self) -> PartialConfig {
214 $i: Some(self.$i.2.clone()),
219 #[allow(unreachable_pub)]
220 pub fn override_value(&mut self, key: &str, val: &str)
226 self.$i.2 = val.parse::<$ty>()
227 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
233 _ => panic!("Unknown config key in override: {}", key)
237 "max_width" | "use_small_heuristics" => self.set_heuristics(),
238 "license_template_path" => self.set_license_template(),
243 #[allow(unreachable_pub)]
244 pub fn is_hidden_option(name: &str) -> bool {
245 const HIDE_OPTIONS: [&str; 4] =
246 ["verbose", "verbose_diff", "file_lines", "width_heuristics"];
247 HIDE_OPTIONS.contains(&name)
250 #[allow(unreachable_pub)]
251 pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
254 $( let max = cmp::max(max, stringify!($i).len()+1); )+
255 let space_str = " ".repeat(max);
256 writeln!(out, "Configuration Options:").unwrap();
258 if $stb || include_unstable {
259 let name_raw = stringify!($i);
261 if !Config::is_hidden_option(name_raw) {
262 let mut name_out = String::with_capacity(max);
263 for _ in name_raw.len()..max-1 {
266 name_out.push_str(name_raw);
269 "{}{} Default: {:?}{}",
273 if !$stb { " (unstable)" } else { "" }).unwrap();
275 writeln!(out, "{}{}", space_str, $dstring).unwrap();
277 writeln!(out).unwrap();
283 fn set_heuristics(&mut self) {
284 if self.use_small_heuristics.2 == Heuristics::Default {
285 let max_width = self.max_width.2;
286 self.set().width_heuristics(WidthHeuristics::scaled(max_width));
287 } else if self.use_small_heuristics.2 == Heuristics::Max {
288 let max_width = self.max_width.2;
289 self.set().width_heuristics(WidthHeuristics::set(max_width));
291 self.set().width_heuristics(WidthHeuristics::null());
295 fn set_license_template(&mut self) {
296 if self.was_set().license_template_path() {
297 let lt_path = self.license_template_path();
298 match license::load_and_compile_template(<_path) {
299 Ok(re) => self.license_template = Some(re),
300 Err(msg) => eprintln!("Warning for license template file {:?}: {}",
306 fn set_ignore(&mut self, dir: &Path) {
307 self.ignore.2.add_prefix(dir);
310 #[allow(unreachable_pub)]
311 /// Returns `true` if the config key was explicitly set and is the default value.
312 pub fn is_default(&self, key: &str) -> bool {
314 if let stringify!($i) = key {
315 return self.$i.1 && self.$i.2 == $def;
322 // Template for the default configuration
323 impl Default for Config {
324 fn default() -> Config {
326 license_template: None,
328 $i: (Cell::new(false), false, $def, $stb),