Skip to content

Commit b483f4c

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 #434 refs #434
1 parent ee32615 commit b483f4c

File tree

3 files changed

+124
-28
lines changed

3 files changed

+124
-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: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -196,41 +196,100 @@ 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+
/// use config::{Config, PartialConfig};
211+
/// let toml_str = r#"
212+
/// ideal_width = 72
213+
/// "#;
214+
///
215+
/// let partial: PartialConfig = toml::decode_str(toml_str);
216+
/// ```
217+
///
218+
/// Later, merge the `PartialConfig` into the default `Config`:
219+
///
220+
/// ```ignore
221+
/// # extern crate toml;
222+
/// # use config::{Config, PartialConfig};
223+
/// # let toml_str = r#"
224+
/// # ideal_width = 72
225+
/// # "#;
226+
///
227+
/// let partial: PartialConfig = toml::decode_str(toml_str);
228+
/// let config = Config::Default().merge(partial);
229+
/// assert_eq!(72, config.ideal_width);
230+
/// ```
204231
#[derive(RustcDecodable, Clone)]
205-
pub struct ParsedConfig {
232+
pub struct PartialConfig {
206233
$(pub $i: Option<$ty>),+
207234
}
208235

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

211-
fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
282+
/// Merge `partial` into `self, overwriting fields in `self` with any non-`None` fields
283+
/// in `partial`.
284+
pub fn merge(mut self, partial: &PartialConfig) -> Config {
212285
$(
213-
if let Some(val) = parsed.$i {
286+
if let Some(val) = partial.$i {
214287
self.$i = val;
215288
}
216289
)+
217290
self
218291
}
219292

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-
234293
pub fn override_value(&mut self, key: &str, val: &str) {
235294
match key {
236295
$(
@@ -347,3 +406,37 @@ create_config! {
347406
write_mode: WriteMode, WriteMode::Default,
348407
"What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
349408
}
409+
410+
#[cfg(test)]
411+
mod tests {
412+
use super::*;
413+
#[test]
414+
fn test_config_merge_overrides() {
415+
let config = Config::default().merge(&PartialConfig {
416+
ideal_width: Some(37),
417+
..PartialConfig::default()
418+
});
419+
assert_eq!(37, config.ideal_width);
420+
}
421+
422+
#[test]
423+
fn test_partial_config_merge_overrides() {
424+
let mut config = PartialConfig::default();
425+
config.merge(&PartialConfig { ideal_width: Some(37), ..PartialConfig::default() });
426+
assert_eq!(Some(37), config.ideal_width);
427+
}
428+
429+
#[test]
430+
fn test_config_merge_does_not_override_if_none() {
431+
let mut config = Config { ideal_width: 37, ..Config::default() };
432+
config = config.merge(&PartialConfig::new());
433+
assert_eq!(37, config.ideal_width);
434+
}
435+
436+
#[test]
437+
fn test_partial_config_merge_does_not_override_if_none() {
438+
let mut config = PartialConfig { ideal_width: Some(37), ..PartialConfig::default() };
439+
config.merge(&PartialConfig::new());
440+
assert_eq!(Some(37), config.ideal_width);
441+
}
442+
}

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)