Skip to content

Commit d0e4db9

Browse files
committed
config: Introduce a mergeable config
This commit replaces `ParsedConfig` with a `PartialConfig` that can be merged into a `Config` or another `PartialConfig`. This provides a unified place for configuration that is passed through rustfmt, and a mechanism for overriding settings from other sources. Expected uses: - overriding config file options from command line options - adding options that do not make sense in the config file, such as line ranges to restrict formatting to; see rust-lang#434 refs rust-lang#434
1 parent ee32615 commit d0e4db9

File tree

3 files changed

+121
-28
lines changed

3 files changed

+121
-28
lines changed

src/bin/rustfmt.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extern crate env_logger;
1818
extern crate getopts;
1919

2020
use rustfmt::{run, run_from_stdin};
21-
use rustfmt::config::{Config, WriteMode};
21+
use rustfmt::config::{Config, PartialConfig, WriteMode};
2222

2323
use std::env;
2424
use std::fs::{self, File};
@@ -92,7 +92,8 @@ fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
9292
let mut file = try!(File::open(&path));
9393
let mut toml = String::new();
9494
try!(file.read_to_string(&mut toml));
95-
Ok((Config::from_toml(&toml), Some(path)))
95+
let parsed_config: PartialConfig = toml::decode_str(&toml).expect("Failed to parse config");
96+
Ok((Config::from(parsed_config), Some(path)))
9697
}
9798

9899
fn update_config(config: &mut Config, matches: &Matches) {

src/config.rs

Lines changed: 112 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -196,41 +196,97 @@ macro_rules! create_config {
196196
$(pub $i: $ty),+
197197
}
198198

199-
// Just like the Config struct but with each property wrapped
200-
// as Option<T>. This is used to parse a rustfmt.toml that doesn't
201-
// specity all properties of `Config`.
202-
// We first parse into `ParsedConfig`, then create a default `Config`
203-
// and overwrite the properties with corresponding values from `ParsedConfig`
199+
/// Equivalent to `Config` except that each field is wrapped in an `Option`.
200+
///
201+
/// This can be decoded into from TOML, and then later merged into a `Config` or another
202+
/// `PartialConfig`.
203+
///
204+
/// # Examples
205+
///
206+
/// Decode a TOML value into a `PartialConfig`:
207+
///
208+
/// ```ignore
209+
/// extern crate toml;
210+
/// let toml_str = r#"
211+
/// ideal_width = 72
212+
/// "#;
213+
///
214+
/// let partial: PartialConfig = toml::decode_str(toml_str);
215+
/// ```
216+
///
217+
/// Later, merge the `PartialConfig` into the default `Config`:
218+
///
219+
/// ```ignore
220+
/// # extern crate toml;
221+
/// # use config::{Config, PartialConfig};
222+
///
223+
/// # let toml_value = "abc = 3".parse().expect("Could not parse TOML");
224+
/// # let partial: PartialConfig = toml::decode(toml_value);
225+
/// let config = Config::Default().merge(partial);
226+
/// assert_eq!(72, config.ideal_width);
227+
/// ```
204228
#[derive(RustcDecodable, Clone)]
205-
pub struct ParsedConfig {
229+
pub struct PartialConfig {
206230
$(pub $i: Option<$ty>),+
207231
}
208232

233+
impl PartialConfig {
234+
235+
/// Create a `PartialConfig` with all fields set to `None`.
236+
pub fn new() -> PartialConfig {
237+
PartialConfig {
238+
$(
239+
$i: None,
240+
)+
241+
}
242+
243+
}
244+
245+
/// Merge `other` into `self, overwriting fields in `self` with any non-`None` fields
246+
/// in `other`.
247+
pub fn merge(&mut self, other: &PartialConfig) -> &mut PartialConfig {
248+
$(
249+
if other.$i.is_some() {
250+
self.$i = other.$i.clone();
251+
}
252+
)+
253+
self
254+
}
255+
}
256+
257+
impl Default for PartialConfig {
258+
fn default() -> PartialConfig {
259+
PartialConfig::new()
260+
}
261+
}
262+
263+
/// Applies settings in `partial` on top of the default `Config`.
264+
impl From<PartialConfig> for Config {
265+
fn from(partial: PartialConfig) -> Config {
266+
Config::default().merge(&partial)
267+
}
268+
}
269+
270+
/// Applies settings in `partial` on top of the default `Config`.
271+
impl<'a> From<&'a PartialConfig> for Config {
272+
fn from(partial: &'a PartialConfig) -> Config {
273+
Config::default().merge(partial)
274+
}
275+
}
276+
209277
impl Config {
210278

211-
fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
279+
/// Merge `partial` into `self, overwriting fields in `self` with any non-`None` fields
280+
/// in `partial`.
281+
pub fn merge(mut self, partial: &PartialConfig) -> Config {
212282
$(
213-
if let Some(val) = parsed.$i {
283+
if let Some(val) = partial.$i {
214284
self.$i = val;
215285
}
216286
)+
217287
self
218288
}
219289

220-
pub fn from_toml(toml: &str) -> Config {
221-
let parsed = toml.parse().unwrap();
222-
let parsed_config:ParsedConfig = match toml::decode(parsed) {
223-
Some(decoded) => decoded,
224-
None => {
225-
println!("Decoding config file failed. Config:\n{}", toml);
226-
let parsed: toml::Value = toml.parse().unwrap();
227-
println!("\n\nParsed:\n{:?}", parsed);
228-
panic!();
229-
}
230-
};
231-
Config::default().fill_from_parsed_config(parsed_config)
232-
}
233-
234290
pub fn override_value(&mut self, key: &str, val: &str) {
235291
match key {
236292
$(
@@ -347,3 +403,37 @@ create_config! {
347403
write_mode: WriteMode, WriteMode::Default,
348404
"What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
349405
}
406+
407+
#[cfg(test)]
408+
mod tests {
409+
use super::*;
410+
#[test]
411+
fn test_config_merge_overrides() {
412+
let config = Config::default().merge(&PartialConfig {
413+
ideal_width: Some(37),
414+
..PartialConfig::default()
415+
});
416+
assert_eq!(37, config.ideal_width);
417+
}
418+
419+
#[test]
420+
fn test_partial_config_merge_overrides() {
421+
let mut config = PartialConfig::default();
422+
config.merge(&PartialConfig { ideal_width: Some(37), ..PartialConfig::default() });
423+
assert_eq!(Some(37), config.ideal_width);
424+
}
425+
426+
#[test]
427+
fn test_config_merge_does_not_override_if_none() {
428+
let mut config = Config { ideal_width: 37, ..Config::default() };
429+
config = config.merge(&PartialConfig::new());
430+
assert_eq!(37, config.ideal_width);
431+
}
432+
433+
#[test]
434+
fn test_partial_config_merge_does_not_override_if_none() {
435+
let mut config = PartialConfig { ideal_width: Some(37), ..PartialConfig::default() };
436+
config.merge(&PartialConfig::new());
437+
assert_eq!(Some(37), config.ideal_width);
438+
}
439+
}

tests/system.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern crate rustfmt;
1212
extern crate diff;
1313
extern crate regex;
1414
extern crate term;
15+
extern crate toml;
1516

1617
use std::collections::HashMap;
1718
use std::fs;
@@ -20,7 +21,7 @@ use std::path::Path;
2021

2122
use rustfmt::*;
2223
use rustfmt::filemap::{write_system_newlines, FileMap};
23-
use rustfmt::config::{Config, ReportTactic, WriteMode};
24+
use rustfmt::config::{Config, PartialConfig, ReportTactic, WriteMode};
2425
use rustfmt::rustfmt_diff::*;
2526

2627
static DIFF_CONTEXT_SIZE: usize = 3;
@@ -232,10 +233,11 @@ fn get_config(config_file: Option<&str>) -> Config {
232233
};
233234

234235
let mut def_config_file = fs::File::open(config_file_name).expect("Couldn't open config");
235-
let mut def_config = String::new();
236-
def_config_file.read_to_string(&mut def_config).expect("Couldn't read config");
236+
let mut buf = String::new();
237+
def_config_file.read_to_string(&mut buf).expect("Couldn't read config");
237238

238-
Config::from_toml(&def_config)
239+
let def_config: PartialConfig = toml::decode_str(&buf).expect("Failed to parse config");
240+
Config::from(def_config)
239241
}
240242

241243
// Reads significant comments of the form: // rustfmt-key: value

0 commit comments

Comments
 (0)