pbjson_types/
struct.rs

1use crate::Struct;
2
3use alloc::string::String;
4
5#[cfg(feature = "std")]
6impl From<std::collections::HashMap<String, crate::Value>> for Struct {
7    fn from(fields: std::collections::HashMap<String, crate::Value>) -> Self {
8        Self { fields }
9    }
10}
11
12#[cfg(not(feature = "std"))]
13impl From<alloc::collections::BTreeMap<String, crate::Value>> for Struct {
14    fn from(fields: alloc::collections::BTreeMap<String, crate::Value>) -> Self {
15        Self { fields }
16    }
17}
18
19impl FromIterator<(String, crate::Value)> for Struct {
20    fn from_iter<T>(iter: T) -> Self
21    where
22        T: IntoIterator<Item = (String, crate::Value)>,
23    {
24        Self {
25            fields: iter.into_iter().collect(),
26        }
27    }
28}
29
30impl serde::Serialize for Struct {
31    fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
32    where
33        S: serde::Serializer,
34    {
35        self.fields.serialize(ser)
36    }
37}
38
39impl<'de> serde::Deserialize<'de> for Struct {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: serde::Deserializer<'de>,
43    {
44        deserializer.deserialize_map(StructVisitor)
45    }
46}
47
48struct StructVisitor;
49
50impl<'de> serde::de::Visitor<'de> for StructVisitor {
51    type Value = Struct;
52
53    fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        formatter.write_str("google.protobuf.Struct")
55    }
56
57    fn visit_map<A>(self, mut map_access: A) -> Result<Self::Value, A::Error>
58    where
59        A: serde::de::MapAccess<'de>,
60    {
61        #[cfg(feature = "std")]
62        let mut map = std::collections::HashMap::new();
63
64        #[cfg(not(feature = "std"))]
65        let mut map = alloc::collections::BTreeMap::new();
66
67        while let Some((key, value)) = map_access.next_entry()? {
68            map.insert(key, value);
69        }
70
71        Ok(map.into())
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[cfg(feature = "std")]
80    #[test]
81    fn it_works() {
82        let map: crate::Struct = std::collections::HashMap::from([
83            (String::from("bool"), crate::Value::from(true)),
84            (
85                String::from("unit"),
86                crate::value::Kind::NullValue(0).into(),
87            ),
88            (String::from("number"), 5.0.into()),
89            (String::from("string"), "string".into()),
90            (String::from("list"), vec![1.0.into(), 2.0.into()].into()),
91            (
92                String::from("map"),
93                std::collections::HashMap::from([(String::from("key"), "value".into())]).into(),
94            ),
95        ])
96        .into();
97
98        assert_eq!(
99            serde_json::to_value(map).unwrap(),
100            serde_json::json!({
101                "bool": true,
102                "unit": null,
103                "number": 5.0,
104                "string": "string",
105                "list": [1.0, 2.0],
106                "map": {
107                    "key": "value",
108                }
109            })
110        );
111    }
112
113    #[cfg(not(feature = "std"))]
114    #[test]
115    fn it_works_on_no_std() {
116        let btree_map = alloc::collections::BTreeMap::from([
117            (String::from("bool"), crate::Value::from(true)),
118            (
119                String::from("unit"),
120                crate::value::Kind::NullValue(0).into(),
121            ),
122            (String::from("number"), 5.0.into()),
123            (String::from("string"), "string".into()),
124            (
125                String::from("list"),
126                alloc::vec![1.0.into(), 2.0.into()].into(),
127            ),
128            (
129                String::from("map"),
130                alloc::collections::BTreeMap::from([(String::from("key"), "value".into())]).into(),
131            ),
132        ]);
133
134        let map = crate::Struct::from(btree_map);
135
136        let json_string = r#"{"bool":true,"list":[1.0,2.0],"number":5.0,"string":"string","unit":null,"map":{"key":"value"}}"#;
137
138        assert_eq!(
139            serde_json::to_value(map).unwrap(),
140            serde_json::from_str::<serde_json::Value>(json_string).unwrap()
141        );
142    }
143}