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}