Repository: mehcode/config-rs (rust)

Issue:
When no configs are present, Config::new() should default to the structs default
In an application, sometimes there is no config present and you would just like to use the default values for your struct. If this is the case currently, you get an obtuse error like: `invalid type: unit value, expected struct Settings`. As an example usage:

```
#[derive(Debug, Deserialize)]
pub struct Settings {
    db_host: string,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            db_host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
        }
    }
}

impl Settings {
    pub fn new() -> Result<Self, ConfigError> {
        let mut s = Config::new();
       // hand waiving pseudo code
        if config_file.present() {
           s.merge(File::with_name(config_file))?;
       }
       s.try_into()
}
```

Pull request:
Fix defaults serialization and 'invalid type: unit value' deserialization error (#60)
A second attempt to fix #60  

Dupe of #95, but tries to follow the [feedback](https://github.com/mehcode/config-rs/pull/95#issuecomment-473707753). 

## Background

As @vorner [wrote](https://github.com/mehcode/config-rs/issues/60#issuecomment-413663545):
> I think there are two issues here, aren't there?
>
> First one is how to use defaults ‒ and the answer for that one is to use the serde's default handling (eg. the default attribute on fields).
>
> The other problem is the annoying unit value error. I managed to work around that one by merging an empty source as the first one, eg config.merge(File::from_str("", FileFormat::Toml))?. Could something like this be already done as part of the constructor? I mean, make sure whatever is tried to be deserialized looks like an empty map to serde, not unit.


## Solution

1. The first issue is solved by providing two new methods:
    - `set_defaults(&mut self, val: &T) -> Result<&mut Self>`, `where T: Serialize`, which allows to mutate defaults values of `Config` by serializing them from given value.
    - `try_defaults_from(val: &T) -> Result<Self>`, `where T: Serialize`, which is the brother of `try_from()` but operates with `defaults` (not `overrides`).

2. The second issue is solved by creating empty `ValueKind::Table` rather than `ValueKind::Nil` as @derekdreery [pointed out](https://github.com/mehcode/config-rs/issues/60#issuecomment-473705663).


## Checklist

- [x] 1. Code style conformed.
- [x] 2. Tests are added.
- [x] 3. Documentation is added/corrected.

Patch:
diff --git a/src/config.rs b/src/config.rs
index f19e59a..fa43e2a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -10,7 +10,7 @@ use ser::ConfigSerializer;
 use source::Source;
 
 use path;
-use value::{Value, ValueKind, ValueWithKey};
+use value::{Table, Value, ValueKind, ValueWithKey};
 
 #[derive(Clone, Debug)]
 enum ConfigKind {
@@ -49,7 +49,12 @@ pub struct Config {
 
 impl Config {
     pub fn new() -> Self {
-        Config::default()
+        Self {
+            kind: ConfigKind::default(),
+            // Config root should be instantiated as an empty table
+            // to avoid deserialization errors.
+            cache: Value::new(None, Table::new()),
+        }
     }
 
     /// Merge in a configuration property source.
@@ -129,6 +134,26 @@ impl Config {
         self.refresh()
     }
 
+    /// Set the configuration defaults by serializing them from given value.
+    pub fn set_defaults<T>(&mut self, value: &T) -> Result<&mut Config>
+    where
+        T: Serialize,
+    {
+        match self.kind {
+            ConfigKind::Mutable {
+                ref mut defaults, ..
+            } => {
+                for (key, val) in Self::try_from(&value)?.collect()? {
+                    defaults.insert(key.parse()?, val);
+                }
+            }
+
+            ConfigKind::Frozen => return Err(ConfigError::Frozen),
+        }
+
+        self.refresh()
+    }
+
     pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
     where
         T: Into<Value>,
@@ -192,13 +217,21 @@ impl Config {
         T::deserialize(self)
     }
 
-    /// Attempt to deserialize the entire configuration into the requested type.
+    /// Attempt to serialize the entire configuration from the given type.
     pub fn try_from<T: Serialize>(from: &T) -> Result<Self> {
         let mut serializer = ConfigSerializer::default();
         from.serialize(&mut serializer)?;
         Ok(serializer.output)
     }
 
+    /// Attempt to serialize the entire configuration from the given type
+    /// as default values.
+    pub fn try_defaults_from<T: Serialize>(from: &T) -> Result<Self> {
+        let mut c = Self::new();
+        c.set_defaults(from)?;
+        Ok(c)
+    }
+
     #[deprecated(since = "0.7.0", note = "please use 'try_into' instead")]
     pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result<T> {
         self.try_into()


Test patch:
diff --git a/tests/defaults.rs b/tests/defaults.rs
new file mode 100644
index 0000000..b00da29
--- /dev/null
+++ b/tests/defaults.rs
@@ -0,0 +1,36 @@
+extern crate config;
+
+#[macro_use]
+extern crate serde_derive;
+
+use config::*;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Settings {
+    pub db_host: String,
+}
+
+impl Default for Settings {
+    fn default() -> Self {
+        Settings {
+            db_host: String::from("default"),
+        }
+    }
+}
+
+#[test]
+fn set_defaults() {
+    let mut c = Config::new();
+    c.set_defaults(&Settings::default())
+        .expect("Setting defaults failed");
+    let s: Settings = c.try_into().expect("Deserialization failed");
+
+    assert_eq!(s.db_host, "default");
+}
+
+#[test]
+fn try_from_defaults() {
+    let c = Config::try_from(&Settings::default()).expect("Serialization failed");
+    let s: Settings = c.try_into().expect("Deserialization failed");
+    assert_eq!(s.db_host, "default");
+}
diff --git a/tests/empty.rs b/tests/empty.rs
new file mode 100644
index 0000000..1f56d38
--- /dev/null
+++ b/tests/empty.rs
@@ -0,0 +1,21 @@
+extern crate config;
+
+#[macro_use]
+extern crate serde_derive;
+
+use config::*;
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Settings {
+    #[serde(skip)]
+    foo: isize,
+    #[serde(skip)]
+    bar: u8,
+}
+
+#[test]
+fn empty_deserializes() {
+    let s: Settings = Config::new().try_into().expect("Deserialization failed");
+    assert_eq!(s.foo, 0);
+    assert_eq!(s.bar, 0);
+}