Repository: martinohmann/hcl-rs (rust)

Issue:
Expected option when passing an option type, instead of type inside of option
```rust
// ser.rs
#[derive(Deserialize)]
pub struct Project {
    pub proj_type: String,
    pub spec: Option<PathBuf>,
    pub dockerfile: Option<PathBuf>,
    pub scripts: Option<Vec<Script>>,
}

#[derive(Deserialize)]
pub struct Script {
    pub name: String,
    pub command: String,
}

// function to load HCL config here...
```


```hcl
// conf.hcl
project "a" {
    proj_type = "generic"
    spec = "./test.spec"
}
```

returns error
```
Message { msg: "invalid type: string \"./test.spec\", expected option", location: None }
```

Pull request:
fix: implement `deserialize_option` for `ValueDeserializer`
Fixes #44

Deserialization into custom struct with `Option<T>` fields caused an
error:

```
Message { msg: "invalid type: string \"./test.spec\", expected option", location: None }
```

The previous implementation of `deserialize_option` just forwarded to `deserialize_any` which was incorrect.

Patch:
diff --git a/src/value/de.rs b/src/value/de.rs
index ae9c2d8..4610ec2 100644
--- a/src/value/de.rs
+++ b/src/value/de.rs
@@ -131,6 +131,16 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer {
         }
     }
 
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
+    where
+        V: Visitor<'de>,
+    {
+        match self.value {
+            Value::Null => visitor.visit_none(),
+            _ => visitor.visit_some(self),
+        }
+    }
+
     fn deserialize_enum<V>(
         self,
         _name: &'static str,
@@ -149,7 +159,7 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer {
 
     forward_to_deserialize_any! {
         bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
-        bytes byte_buf option unit unit_struct newtype_struct seq tuple
+        bytes byte_buf unit unit_struct newtype_struct seq tuple
         tuple_struct map struct identifier ignored_any
     }
 }


Test patch:
diff --git a/src/de/tests.rs b/src/de/tests.rs
index 55b0fa7..549f9a3 100644
--- a/src/de/tests.rs
+++ b/src/de/tests.rs
@@ -269,3 +269,57 @@ fn deserialize_terraform() {
 
     assert_eq!(expected, body);
 }
+
+// https://github.com/martinohmann/hcl-rs/issues/44
+#[test]
+fn issue_44() {
+    use std::collections::HashMap;
+    use std::path::PathBuf;
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    pub struct Config {
+        #[serde(rename = "project")]
+        pub projects: HashMap<String, Project>,
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    pub struct Project {
+        pub proj_type: String,
+        pub spec: Option<PathBuf>,
+        pub dockerfile: Option<PathBuf>,
+        pub scripts: Option<Vec<Script>>,
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    pub struct Script {
+        pub name: String,
+        pub command: String,
+    }
+
+    let input = r#"
+        project "a" {
+            proj_type = "generic"
+            spec = "./test.spec"
+        }
+    "#;
+
+    let config: Config = crate::from_str(input).unwrap();
+
+    let expected = Config {
+        projects: {
+            let mut map = HashMap::new();
+            map.insert(
+                "a".into(),
+                Project {
+                    proj_type: "generic".into(),
+                    spec: Some(PathBuf::from("./test.spec")),
+                    dockerfile: None,
+                    scripts: None,
+                },
+            );
+            map
+        },
+    };
+
+    assert_eq!(config, expected);
+}