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//! Implement the well-known types for structured, yet dynamically typed
16//! messages. These types are a representation of JSON objects, lists, and
17//! values as Protobuf messages. We have taken some (allowed) liberty in their
18//! representation for Rust. We map them directly to the [serde_json] types,
19//! except for `NullValue` where there is no corresponding type in serde.
20//!
21//! Services specified using Protobuf files may use `google.protobuf.Struct`,
22//! `google.protobuf.Value`, `google.protobuf.ListValue`, and/or
23//! `google.protobuf.NullValue` as part of their interface specification.
24
25/// Protobuf (and consequently the Google Cloud APIs) use `Struct` to represent
26/// JSON objects. We need a type that can be referenced from the generated code.
27pub type Struct = serde_json::Map<String, serde_json::Value>;
28
29/// Protobuf (and consequently the Google Cloud APIs) use `Value` to represent
30/// JSON values. We need a type that can be referenced from the generated code.
31pub type Value = serde_json::Value;
32
33/// Protobuf (and consequently the Google Cloud APIs) use `ListValue` to
34/// represent a list of JSON values. We need a type that can be referenced
35/// from the generated code.
36pub type ListValue = Vec<serde_json::Value>;
37
38/// A message representing the `null` value. We need a type that can be
39/// referenced from the generated code.
40#[derive(Clone, Debug, Default, PartialEq)]
41pub struct NullValue;
42
43impl crate::message::Message for Struct {
44    fn typename() -> &'static str {
45        "type.googleapis.com/google.protobuf.Struct"
46    }
47    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
48    where
49        Self: serde::ser::Serialize + Sized,
50    {
51        let map: crate::message::Map = [
52            ("@type", Value::String(Self::typename().to_string())),
53            ("value", Value::Object(self.clone())),
54        ]
55        .into_iter()
56        .map(|(k, v)| (k.to_string(), v))
57        .collect();
58        Ok(map)
59    }
60    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
61    where
62        Self: serde::de::DeserializeOwned,
63    {
64        map.get("value")
65            .and_then(|v| v.as_object())
66            .cloned()
67            .ok_or_else(crate::message::missing_value_field)
68    }
69}
70
71impl crate::message::Message for Value {
72    fn typename() -> &'static str {
73        "type.googleapis.com/google.protobuf.Value"
74    }
75    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
76    where
77        Self: serde::ser::Serialize + Sized,
78    {
79        let map: crate::message::Map = [
80            ("@type", Value::String(Self::typename().to_string())),
81            ("value", self.clone()),
82        ]
83        .into_iter()
84        .map(|(k, v)| (k.to_string(), v))
85        .collect();
86        Ok(map)
87    }
88    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
89    where
90        Self: serde::de::DeserializeOwned,
91    {
92        map.get("value")
93            .cloned()
94            .ok_or_else(crate::message::missing_value_field)
95    }
96}
97
98impl crate::message::Message for ListValue {
99    fn typename() -> &'static str {
100        "type.googleapis.com/google.protobuf.ListValue"
101    }
102    fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
103    where
104        Self: serde::ser::Serialize + Sized,
105    {
106        let map: crate::message::Map = [
107            ("@type", Value::String(Self::typename().to_string())),
108            ("value", Value::Array(self.clone())),
109        ]
110        .into_iter()
111        .map(|(k, v)| (k.to_string(), v))
112        .collect();
113        Ok(map)
114    }
115    fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
116    where
117        Self: serde::de::DeserializeOwned,
118    {
119        map.get("value")
120            .and_then(|v| v.as_array())
121            .cloned()
122            .ok_or_else(crate::message::missing_value_field)
123    }
124}
125
126/// Protobuf represents `NullValue` as an enum. In some contexts, it is
127/// useful to make it behave as if it was.
128impl NullValue {
129    /// Gets the value.
130    pub fn value(&self) -> i32 {
131        0
132    }
133
134    /// Gets the value as a string.
135    pub fn as_str_name(&self) -> std::borrow::Cow<'static, str> {
136        "NULL_VALUE".into()
137    }
138
139    /// Creates a value from the value name
140    pub fn from_str_name(_name: &str) -> Option<Self> {
141        Some(Self)
142    }
143}
144
145impl From<i32> for NullValue {
146    fn from(_value: i32) -> Self {
147        Self
148    }
149}
150
151// This is needed when `NullValue` is used in a Protobuf-generated message.
152impl From<NullValue> for i32 {
153    fn from(_value: NullValue) -> Self {
154        Default::default()
155    }
156}
157
158impl From<NullValue> for serde_json::Value {
159    fn from(_value: NullValue) -> Self {
160        Default::default()
161    }
162}
163
164impl From<&NullValue> for serde_json::Value {
165    fn from(_value: &NullValue) -> Self {
166        Default::default()
167    }
168}
169
170/// Implement [`serde`](::serde) serialization for [NullValue].
171impl serde::ser::Serialize for NullValue {
172    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
173    where
174        S: serde::ser::Serializer,
175    {
176        serde_json::Value::Null.serialize(serializer)
177    }
178}
179
180/// Implement [`serde`](::serde) deserialization for [NullValue].
181impl<'de> serde::de::Deserialize<'de> for NullValue {
182    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183    where
184        D: serde::Deserializer<'de>,
185    {
186        let value = Value::deserialize(deserializer)?;
187        if value.is_null() {
188            return Ok(NullValue);
189        }
190        Err(serde::de::Error::invalid_value(
191            serde::de::Unexpected::Other(&value.to_string()),
192            &"a null JSON object",
193        ))
194    }
195}
196
197// Verify the different value types work with `crate::Any`.
198#[cfg(test)]
199mod any_tests {
200    use super::*;
201    use crate::Any;
202    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
203
204    #[test]
205    fn test_null_value_interface() {
206        let input = NullValue;
207        assert_eq!(input.value(), NullValue.value());
208        assert_eq!(input.as_str_name().as_ref(), "NULL_VALUE");
209        assert_eq!(NullValue::from_str_name("NULL_VALUE"), Some(NullValue));
210        assert_eq!(NullValue::from(0), NullValue);
211    }
212
213    #[test]
214    fn test_serde_null_value() -> Result {
215        let input = Value::Null;
216        let any = Any::try_from(&input)?;
217        let got = serde_json::to_value(&any)?;
218        let want = serde_json::json!({
219            "@type": "type.googleapis.com/google.protobuf.Value",
220            "value": null
221        });
222        assert_eq!(got, want);
223        let output = any.try_into_message::<Value>()?;
224        assert_eq!(output, input);
225        Ok(())
226    }
227
228    #[test]
229    fn test_bool_value() -> Result {
230        let input = Value::Bool(true);
231        let any = Any::try_from(&input)?;
232        let got = serde_json::to_value(&any)?;
233        let want = serde_json::json!({
234            "@type": "type.googleapis.com/google.protobuf.Value",
235            "value": true
236        });
237        assert_eq!(got, want);
238        let output = any.try_into_message::<Value>()?;
239        assert_eq!(output, input);
240        Ok(())
241    }
242
243    #[test]
244    fn test_number_value() -> Result {
245        let input = serde_json::json!(1234.5);
246        let any = Any::try_from(&input)?;
247        let got = serde_json::to_value(&any)?;
248        let want = serde_json::json!({
249            "@type": "type.googleapis.com/google.protobuf.Value",
250            "value": 1234.5
251        });
252        assert_eq!(got, want);
253        let output = any.try_into_message::<Value>()?;
254        assert_eq!(output, input);
255        Ok(())
256    }
257
258    #[test]
259    fn test_string_value() -> Result {
260        let input = Value::String(String::from("abc123"));
261        let any = Any::try_from(&input)?;
262        let got = serde_json::to_value(&any)?;
263        let want = serde_json::json!({
264            "@type": "type.googleapis.com/google.protobuf.Value",
265            "value": "abc123"
266        });
267        assert_eq!(got, want);
268        let output = any.try_into_message::<Value>()?;
269        assert_eq!(output, input);
270        Ok(())
271    }
272
273    #[test]
274    fn test_struct_in_value() -> Result {
275        let structz = serde_json::json!({
276            "fieldA": "123",
277            "fieldB": {
278                "fieldC": ["a", "b", "c"]
279            }
280        })
281        .as_object()
282        .cloned()
283        .unwrap();
284
285        let input = Value::Object(structz);
286        let any = Any::try_from(&input)?;
287        let got = serde_json::to_value(&any)?;
288        let want = serde_json::json!({
289            "@type": "type.googleapis.com/google.protobuf.Value",
290            "value": {
291                "fieldA": "123",
292                "fieldB": {
293                    "fieldC": ["a", "b", "c"]
294                }
295            }
296        });
297        assert_eq!(got, want);
298        let output = any.try_into_message::<Value>()?;
299        assert_eq!(output, input);
300        Ok(())
301    }
302
303    #[test]
304    fn test_list_value() -> Result {
305        let input = serde_json::json!([1, 2, 3, 4, "abc"])
306            .as_array()
307            .cloned()
308            .unwrap();
309        let any = Any::try_from(&input)?;
310        let got = serde_json::to_value(&any)?;
311        let want = serde_json::json!({
312            "@type": "type.googleapis.com/google.protobuf.ListValue",
313            "value": [1, 2, 3, 4, "abc"],
314        });
315        assert_eq!(got, want);
316        let output = any.try_into_message::<ListValue>()?;
317        assert_eq!(output, input);
318        Ok(())
319    }
320
321    #[test]
322    fn test_struct() -> Result {
323        let input = serde_json::json!({
324            "fieldA": "a_value",
325            "fieldB": {
326                "fieldC": [1, 2, 3, 4, "abc"],
327            },
328        })
329        .as_object()
330        .cloned()
331        .unwrap();
332        let any = Any::try_from(&input)?;
333        let got = serde_json::to_value(&any)?;
334        let want = serde_json::json!({
335            "@type": "type.googleapis.com/google.protobuf.Struct",
336            "value": {
337                "fieldA": "a_value",
338                "fieldB": {
339                    "fieldC": [1, 2, 3, 4, "abc"],
340                },
341            },
342        });
343        assert_eq!(got, want);
344        let output = any.try_into_message::<Struct>()?;
345        assert_eq!(output, input);
346        Ok(())
347    }
348}
349
350#[cfg(test)]
351mod null_value_tests {
352    use super::*;
353    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
354
355    #[test]
356    fn test_wkt_null_value_to_value() {
357        let input = NullValue;
358        let value = Value::from(input);
359        assert!(value.is_null(), "{value:?}");
360
361        let input = &NullValue;
362        let value = Value::from(input);
363        assert!(value.is_null(), "{value:?}");
364    }
365
366    #[test]
367    fn test_i32_from_null_value() {
368        let got = i32::from(NullValue);
369        assert_eq!(got, 0);
370    }
371
372    #[test]
373    fn test_serde_from_null_value() {
374        let got = serde_json::Value::from(NullValue);
375        assert_eq!(got, serde_json::Value::Null);
376        let got = serde_json::Value::from(&NullValue);
377        assert_eq!(got, serde_json::Value::Null);
378    }
379
380    #[test]
381    fn test_null_value_serialize() -> Result {
382        let input = NullValue;
383        let got = serde_json::to_string(&input)?;
384        assert_eq!(got, "null");
385        Ok(())
386    }
387
388    #[test]
389    fn test_null_value_deserialize() -> Result {
390        let input = "null";
391        let got = serde_json::from_str::<NullValue>(input)?;
392        assert_eq!(got, NullValue);
393
394        let input = "123";
395        let got = serde_json::from_str::<NullValue>(input);
396        assert!(got.is_err(), "{got:?}");
397
398        let input = "\"123";
399        let got = serde_json::from_str::<NullValue>(input);
400        assert!(got.is_err(), "{got:?}");
401        Ok(())
402    }
403}