Repository: ron-rs/ron (rust)

Issue:
Ergonomics of optional fields
If I have a struct like

```rust
#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
    pub font: Option<PathBuf>, // <- optional field
    pub other_value: u8,
}
```

I can omit the field in `.ron` file to get `None`:

```json
(
    other_value: 0,
)
```

But I'm forced to write `Some()` around the actual value which clutters up the config:

```json
(
    font: Some("OpenSans-Regular.ttf"), // :(
    // font: "OpenSans-Regular.ttf", // I want this
    other_value: 0,
)
```

Seems like an ergonomic problem to me :(

See https://gitter.im/ron-rs/ron?at=59d228bd614889d47565733d

Pull request:
Allow implicit Some
Fixes #55 

Patch:
diff --git a/README.md b/README.md
index 1df3b142..e24dbb22 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ Scene( // class name is optional
 
 The new format uses `(`..`)` brackets for *heterogeneous* structures (classes),
 while preserving the `{`..`}` for maps, and `[`..`]` for *homogeneous* structures (arrays).
-This distinction allows us to solve the biggest problem with JSON.
+This distinction allows to solve the biggest problem with JSON.
 
 Here are the general rules to parse the heterogeneous structures:
 
diff --git a/src/de/mod.rs b/src/de/mod.rs
index 4de9318d..4af6e334 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -228,23 +228,28 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
     fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
         where V: Visitor<'de>
     {
-        if self.bytes.consume("Some") && { self.bytes.skip_ws(); self.bytes.consume("(") } {
-            self.bytes.skip_ws();
+        if self.bytes.consume("None") {
+            visitor.visit_none()
+        } else {
+            if self.bytes.exts.contains(Extensions::IMPLICIT_SOME) {
+                visitor.visit_some(&mut *self)
+            } else {
+                if self.bytes.consume("Some") && { self.bytes.skip_ws(); self.bytes.consume("(") } {
+                    self.bytes.skip_ws();
 
-            let v = visitor.visit_some(&mut *self)?;
+                    let v = visitor.visit_some(&mut *self)?;
 
-            self.bytes.skip_ws();
+                    self.bytes.skip_ws();
 
-            if self.bytes.consume(")") {
-                Ok(v)
-            } else {
-                self.bytes.err(ParseError::ExpectedOptionEnd)
+                    if self.bytes.consume(")") {
+                        Ok(v)
+                    } else {
+                        self.bytes.err(ParseError::ExpectedOptionEnd)
+                    }
+                } else {
+                    self.bytes.err(ParseError::ExpectedOption)
+                }
             }
-
-        } else if self.bytes.consume("None") {
-            visitor.visit_none()
-        } else {
-            self.bytes.err(ParseError::ExpectedOption)
         }
     }
 
diff --git a/src/parse.rs b/src/parse.rs
index 03e1dcbe..89ba5a2f 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -459,6 +459,7 @@ impl<'a> Bytes<'a> {
 bitflags! {
     pub struct Extensions: usize {
         const UNWRAP_NEWTYPES = 0x1;
+        const IMPLICIT_SOME = 0x2;
     }
 }
 
@@ -467,6 +468,7 @@ impl Extensions {
     pub fn from_ident(ident: &[u8]) -> Option<Extensions> {
         match ident {
             b"unwrap_newtypes" => Some(Extensions::UNWRAP_NEWTYPES),
+            b"implicit_some" => Some(Extensions::IMPLICIT_SOME),
             _ => None,
         }
     }


Test patch:
diff --git a/src/de/tests.rs b/src/de/tests.rs
index 951e1059..6e6cddb8 100644
--- a/src/de/tests.rs
+++ b/src/de/tests.rs
@@ -209,3 +209,28 @@ fn uglified_attribute() {
 
     assert_eq!(de, Ok(()));
 }
+
+#[test]
+fn implicit_some() {
+    use serde::de::DeserializeOwned;
+
+    fn de<T: DeserializeOwned>(s: &str) -> Option<T> {
+        let enable = "#![enable(implicit_some)]\n".to_string();
+
+        from_str::<Option<T>>(&(enable + s)).unwrap()
+    }
+
+    assert_eq!(de("'c'"), Some('c'));
+    assert_eq!(de("5"), Some(5));
+    assert_eq!(de("\"Hello\""), Some("Hello".to_owned()));
+    assert_eq!(de("false"), Some(false));
+    assert_eq!(de("MyStruct(x: .4, y: .5)"), Some(MyStruct {
+        x: 0.4,
+        y: 0.5,
+    }));
+
+    assert_eq!(de::<char>("None"), None);
+
+    // Not concise
+    assert_eq!(de::<Option<Option<char>>>("None"), None);
+}
diff --git a/tests/extensions.rs b/tests/extensions.rs
index 7702a930..18f31cc0 100644
--- a/tests/extensions.rs
+++ b/tests/extensions.rs
@@ -54,3 +54,31 @@ fn unwrap_newtypes() {
 
     println!("unwrap_newtypes: {:#?}", d);
 }
+
+const CONFIG_I_S: &str = "
+#![enable(implicit_some)]
+
+(
+    tuple: ((), (0.5), ((), -5)),
+    vec: [
+        None,
+        (),
+        UnitStruct,
+        None,
+        (),
+    ],
+    map: {
+        (7): Bool(true),
+        (9): Chars('x', \"\"),
+        (6): Bool(false),
+        (5): Unit,
+    },
+)
+";
+
+#[test]
+fn implicit_some() {
+    let d: Struct = ron::de::from_str(&CONFIG_I_S).expect("Failed to deserialize");
+
+    println!("implicit_some: {:#?}", d);
+}