cargo_cargofmt/config/
mod.rs

1use std::io;
2use std::path::Path;
3use std::path::PathBuf;
4
5pub mod lists;
6pub mod options;
7
8#[derive(serde::Deserialize)]
9#[serde(default)]
10pub struct Config {
11    pub disable_all_formatting: bool,
12    pub newline_style: options::NewlineStyle,
13    pub format_generated_files: bool,
14    pub generated_marker_line_search_limit: usize,
15    pub blank_lines_lower_bound: usize,
16    pub blank_lines_upper_bound: usize,
17    pub trailing_comma: lists::SeparatorTactic,
18    pub hard_tabs: bool,
19    pub tab_spaces: usize,
20    pub max_width: usize,
21    pub array_width: Option<usize>,
22    pub use_small_heuristics: options::UseSmallHeuristics,
23}
24
25impl Config {
26    /// Returns the effective array width.
27    ///
28    /// If `array_width` is explicitly set, returns that value.
29    /// Otherwise, calculates based on `use_small_heuristics`:
30    /// - `Default`: 60% of `max_width`
31    /// - `Off`: 0 (always vertical)
32    /// - `Max`: `max_width`
33    pub fn array_width(&self) -> usize {
34        self.array_width
35            .unwrap_or_else(|| self.heuristic_width(0.6))
36    }
37
38    fn heuristic_width(&self, percent: f64) -> usize {
39        match self.use_small_heuristics {
40            options::UseSmallHeuristics::Default => (self.max_width as f64 * percent) as usize,
41            options::UseSmallHeuristics::Off => 0,
42            options::UseSmallHeuristics::Max => self.max_width,
43        }
44    }
45}
46
47impl Default for Config {
48    fn default() -> Self {
49        Self {
50            disable_all_formatting: false,
51            newline_style: options::NewlineStyle::default(),
52            format_generated_files: false,
53            generated_marker_line_search_limit: 5,
54            blank_lines_lower_bound: 0,
55            blank_lines_upper_bound: 1,
56            trailing_comma: lists::SeparatorTactic::Vertical,
57            hard_tabs: false,
58            tab_spaces: 4,
59            max_width: 100,
60            array_width: None,
61            use_small_heuristics: options::UseSmallHeuristics::default(),
62        }
63    }
64}
65
66#[tracing::instrument]
67pub fn load_config(search_start: &Path) -> Result<Config, io::Error> {
68    let Some(path) = find_config(search_start) else {
69        return Ok(Config::default());
70    };
71
72    let content = std::fs::read_to_string(&path)?;
73    let config = toml::de::from_str(&content).map_err(io::Error::other)?;
74    Ok(config)
75}
76
77fn find_config(mut path: &Path) -> Option<PathBuf> {
78    if path.is_file() {
79        path = path.parent()?;
80    }
81
82    loop {
83        let mut config_path = path.join("");
84        for name in CONFIG_FILE_NAMES {
85            config_path.set_file_name(name);
86            if config_path.is_file() {
87                return Some(config_path);
88            }
89        }
90
91        path = path.parent()?;
92    }
93}
94
95const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
96
97#[cfg(test)]
98mod test {
99    use super::*;
100
101    #[test]
102    fn max_width() {
103        assert_eq!(Config::default().max_width, 100);
104
105        let config: Config = toml::de::from_str("max_width = 80").unwrap();
106        assert_eq!(config.max_width, 80);
107    }
108
109    #[test]
110    fn array_width_uses_heuristics() {
111        // Default: 60% of max_width
112        let config = Config::default();
113        assert_eq!(config.array_width, None);
114        assert_eq!(config.array_width(), 60); // 60% of 100
115
116        let config: Config = toml::de::from_str("max_width = 80").unwrap();
117        assert_eq!(config.array_width(), 48); // 60% of 80
118
119        // Explicit array_width overrides heuristics
120        let config: Config = toml::de::from_str("max_width = 100\narray_width = 70").unwrap();
121        assert_eq!(config.array_width(), 70);
122    }
123
124    #[test]
125    fn use_small_heuristics_max() {
126        let config: Config =
127            toml::de::from_str("max_width = 100\nuse_small_heuristics = \"Max\"").unwrap();
128        assert_eq!(config.array_width(), 100); // equals max_width
129    }
130
131    #[test]
132    fn use_small_heuristics_off() {
133        let config: Config =
134            toml::de::from_str("max_width = 100\nuse_small_heuristics = \"Off\"").unwrap();
135        assert_eq!(config.array_width(), 0); // always vertical
136    }
137}