@@ -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 \n Parsed:\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+ }
0 commit comments