google_cloud_wkt/
rstruct.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/// Protobuf (and consequently the Google Cloud APIs) use `Struct` to represent
16/// JSON objects. We need a type that might be referenced from the generated
17/// code.
18pub type Struct = serde_json::Map<String, serde_json::Value>;
19
20/// Protobuf (and consequently the Google Cloud APIs) use `Value` to represent
21/// JSON values. We need a type that might be referenced from the generated
22/// code.
23pub type Value = serde_json::Value;
24
25/// Protobuf (and consequently the Google Cloud APIs) use `ListValue` to
26/// represent a list of JSON values. We need a type that might be referenced
27/// from the generated code.
28pub type ListValue = Vec<serde_json::Value>;
29
30impl crate::message::Message for Struct {
31    fn typename() -> &'static str {
32        "type.googleapis.com/google.protobuf.Struct"
33    }
34    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
35    where
36        Self: serde::ser::Serialize + Sized,
37    {
38        let map: crate::message::Map = [
39            ("@type", Value::String(Self::typename().to_string())),
40            ("value", Value::Object(self.clone())),
41        ]
42        .into_iter()
43        .map(|(k, v)| (k.to_string(), v))
44        .collect();
45        Ok(map)
46    }
47    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
48    where
49        Self: serde::de::DeserializeOwned,
50    {
51        map.get("value")
52            .and_then(|v| v.as_object())
53            .cloned()
54            .ok_or_else(crate::message::missing_value_field)
55    }
56}
57
58impl crate::message::Message for Value {
59    fn typename() -> &'static str {
60        "type.googleapis.com/google.protobuf.Value"
61    }
62    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
63    where
64        Self: serde::ser::Serialize + Sized,
65    {
66        let map: crate::message::Map = [
67            ("@type", Value::String(Self::typename().to_string())),
68            ("value", self.clone()),
69        ]
70        .into_iter()
71        .map(|(k, v)| (k.to_string(), v))
72        .collect();
73        Ok(map)
74    }
75    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
76    where
77        Self: serde::de::DeserializeOwned,
78    {
79        map.get("value")
80            .cloned()
81            .ok_or_else(crate::message::missing_value_field)
82    }
83}
84
85impl crate::message::Message for ListValue {
86    fn typename() -> &'static str {
87        "type.googleapis.com/google.protobuf.ListValue"
88    }
89    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
90    where
91        Self: serde::ser::Serialize + Sized,
92    {
93        let map: crate::message::Map = [
94            ("@type", Value::String(Self::typename().to_string())),
95            ("value", Value::Array(self.clone())),
96        ]
97        .into_iter()
98        .map(|(k, v)| (k.to_string(), v))
99        .collect();
100        Ok(map)
101    }
102    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
103    where
104        Self: serde::de::DeserializeOwned,
105    {
106        map.get("value")
107            .and_then(|v| v.as_array())
108            .cloned()
109            .ok_or_else(crate::message::missing_value_field)
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use crate::Any;
117    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
118
119    #[test]
120    fn test_null_value() -> Result {
121        let input = Value::Null;
122        let any = Any::try_from(&input)?;
123        let got = serde_json::to_value(&any)?;
124        let want = serde_json::json!({
125            "@type": "type.googleapis.com/google.protobuf.Value",
126            "value": null
127        });
128        assert_eq!(got, want);
129        let output = any.try_into_message::<Value>()?;
130        assert_eq!(output, input);
131        Ok(())
132    }
133
134    #[test]
135    fn test_bool_value() -> Result {
136        let input = Value::Bool(true);
137        let any = Any::try_from(&input)?;
138        let got = serde_json::to_value(&any)?;
139        let want = serde_json::json!({
140            "@type": "type.googleapis.com/google.protobuf.Value",
141            "value": true
142        });
143        assert_eq!(got, want);
144        let output = any.try_into_message::<Value>()?;
145        assert_eq!(output, input);
146        Ok(())
147    }
148
149    #[test]
150    fn test_number_value() -> Result {
151        let input = serde_json::json!(1234.5);
152        let any = Any::try_from(&input)?;
153        let got = serde_json::to_value(&any)?;
154        let want = serde_json::json!({
155            "@type": "type.googleapis.com/google.protobuf.Value",
156            "value": 1234.5
157        });
158        assert_eq!(got, want);
159        let output = any.try_into_message::<Value>()?;
160        assert_eq!(output, input);
161        Ok(())
162    }
163
164    #[test]
165    fn test_string_value() -> Result {
166        let input = Value::String(String::from("abc123"));
167        let any = Any::try_from(&input)?;
168        let got = serde_json::to_value(&any)?;
169        let want = serde_json::json!({
170            "@type": "type.googleapis.com/google.protobuf.Value",
171            "value": "abc123"
172        });
173        assert_eq!(got, want);
174        let output = any.try_into_message::<Value>()?;
175        assert_eq!(output, input);
176        Ok(())
177    }
178
179    #[test]
180    fn test_struct_in_value() -> Result {
181        let structz = serde_json::json!({
182            "fieldA": "123",
183            "fieldB": {
184                "fieldC": ["a", "b", "c"]
185            }
186        })
187        .as_object()
188        .cloned()
189        .unwrap();
190
191        let input = Value::Object(structz);
192        let any = Any::try_from(&input)?;
193        let got = serde_json::to_value(&any)?;
194        let want = serde_json::json!({
195            "@type": "type.googleapis.com/google.protobuf.Value",
196            "value": {
197                "fieldA": "123",
198                "fieldB": {
199                    "fieldC": ["a", "b", "c"]
200                }
201            }
202        });
203        assert_eq!(got, want);
204        let output = any.try_into_message::<Value>()?;
205        assert_eq!(output, input);
206        Ok(())
207    }
208
209    #[test]
210    fn test_list_value() -> Result {
211        let input = serde_json::json!([1, 2, 3, 4, "abc"])
212            .as_array()
213            .cloned()
214            .unwrap();
215        let any = Any::try_from(&input)?;
216        let got = serde_json::to_value(&any)?;
217        let want = serde_json::json!({
218            "@type": "type.googleapis.com/google.protobuf.ListValue",
219            "value": [1, 2, 3, 4, "abc"],
220        });
221        assert_eq!(got, want);
222        let output = any.try_into_message::<ListValue>()?;
223        assert_eq!(output, input);
224        Ok(())
225    }
226
227    #[test]
228    fn test_struct() -> Result {
229        let input = serde_json::json!({
230            "fieldA": "a_value",
231            "fieldB": {
232                "fieldC": [1, 2, 3, 4, "abc"],
233            },
234        })
235        .as_object()
236        .cloned()
237        .unwrap();
238        let any = Any::try_from(&input)?;
239        let got = serde_json::to_value(&any)?;
240        let want = serde_json::json!({
241            "@type": "type.googleapis.com/google.protobuf.Struct",
242            "value": {
243                "fieldA": "a_value",
244                "fieldB": {
245                    "fieldC": [1, 2, 3, 4, "abc"],
246                },
247            },
248        });
249        assert_eq!(got, want);
250        let output = any.try_into_message::<Struct>()?;
251        assert_eq!(output, input);
252        Ok(())
253    }
254}