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/// The Google Cloud APIs use `google.protobuf.Struct` to represent JSON objects.
26///
27/// The Google Cloud client libraries for Rust map `google.protobuf.Struct` to
28/// this alias.
29pub type Struct = serde_json::Map<String, serde_json::Value>;
30
31/// The Google Cloud APIs use `google.protobuf.Value` to represent JSON values,
32/// including objects, lists, and scalars.
33///
34/// The Google Cloud client libraries for Rust map `google.protobuf.Value` to
35/// this alias.
36pub type Value = serde_json::Value;
37
38/// The Google Cloud APIs use `google.protobuf.ListValue` to represent JSON
39/// to represent lists of JSON values.
40///
41/// The Google Cloud client libraries for Rust map `google.protobuf.ListValue`
42/// to this alias.
43pub type ListValue = Vec<serde_json::Value>;
44
45/// The Google Cloud APIs use `google.protobuf.NullValue` to represent JSON
46/// null values.
47///
48/// The Google Cloud client libraries for Rust map `google.protobuf.NullValue`
49/// to this unit type.
50#[derive(Clone, Debug, Default, PartialEq)]
51pub struct NullValue;
52
53impl crate::message::Message for Struct {
54    fn typename() -> &'static str {
55        "type.googleapis.com/google.protobuf.Struct"
56    }
57
58    #[allow(private_interfaces)]
59    fn serializer() -> impl crate::message::MessageSerializer<Self> {
60        crate::message::ValueSerializer::<Self>::new()
61    }
62}
63
64impl crate::message::Message for Value {
65    fn typename() -> &'static str {
66        "type.googleapis.com/google.protobuf.Value"
67    }
68
69    #[allow(private_interfaces)]
70    fn serializer() -> impl crate::message::MessageSerializer<Self> {
71        crate::message::ValueSerializer::<Self>::new()
72    }
73}
74
75impl crate::message::Message for ListValue {
76    fn typename() -> &'static str {
77        "type.googleapis.com/google.protobuf.ListValue"
78    }
79
80    #[allow(private_interfaces)]
81    fn serializer() -> impl crate::message::MessageSerializer<Self> {
82        crate::message::ValueSerializer::<Self>::new()
83    }
84}
85
86// Protobuf represents `NullValue` as an enum. In some contexts, it is
87// useful to make it behave as if it was.
88impl NullValue {
89    /// Gets the value.
90    pub fn value(&self) -> Option<i32> {
91        Some(0)
92    }
93
94    /// Gets the value as a string.
95    pub fn name(&self) -> Option<&str> {
96        Some("NULL_VALUE")
97    }
98}
99
100impl From<i32> for NullValue {
101    fn from(_value: i32) -> Self {
102        Self
103    }
104}
105
106impl From<&str> for NullValue {
107    fn from(_value: &str) -> Self {
108        Self
109    }
110}
111
112impl From<String> for NullValue {
113    fn from(_value: String) -> Self {
114        Self
115    }
116}
117
118// This is needed when `NullValue` is used in a Protobuf-generated message.
119impl From<NullValue> for i32 {
120    fn from(_value: NullValue) -> Self {
121        Default::default()
122    }
123}
124
125impl From<NullValue> for serde_json::Value {
126    fn from(_value: NullValue) -> Self {
127        Default::default()
128    }
129}
130
131impl From<&NullValue> for serde_json::Value {
132    fn from(_value: &NullValue) -> Self {
133        Default::default()
134    }
135}
136
137/// Implement [`serde`](::serde) serialization for [NullValue].
138#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
139impl serde::ser::Serialize for NullValue {
140    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141    where
142        S: serde::ser::Serializer,
143    {
144        serde_json::Value::Null.serialize(serializer)
145    }
146}
147
148/// Implement [`serde`](::serde) deserialization for [NullValue].
149#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
150impl<'de> serde::de::Deserialize<'de> for NullValue {
151    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152    where
153        D: serde::Deserializer<'de>,
154    {
155        let value = Value::deserialize(deserializer)?;
156        if value.is_null() {
157            return Ok(NullValue);
158        }
159        Err(serde::de::Error::invalid_value(
160            serde::de::Unexpected::Other(&value.to_string()),
161            &"a null JSON object",
162        ))
163    }
164}
165
166// Verify the different value types work with `crate::Any`.
167#[cfg(test)]
168mod any_tests {
169    use super::*;
170    use crate::Any;
171    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
172
173    #[test]
174    fn test_null_value_interface() {
175        let input = NullValue;
176        assert_eq!(input.value(), Some(0));
177        assert_eq!(input.name(), Some("NULL_VALUE"));
178        assert_eq!(NullValue::from("NULL_VALUE"), NullValue);
179        assert_eq!(NullValue::from("NULL_VALUE".to_string()), NullValue);
180        assert_eq!(NullValue::from(0), NullValue);
181    }
182
183    #[test]
184    fn test_serde_null_value() -> Result {
185        let input = Value::Null;
186        let any = Any::from_msg(&input)?;
187        let got = serde_json::to_value(&any)?;
188        let want = serde_json::json!({
189            "@type": "type.googleapis.com/google.protobuf.Value",
190            "value": null
191        });
192        assert_eq!(got, want);
193        let output = any.to_msg::<Value>()?;
194        assert_eq!(output, input);
195        Ok(())
196    }
197
198    #[test]
199    fn test_bool_value() -> Result {
200        let input = Value::Bool(true);
201        let any = Any::from_msg(&input)?;
202        let got = serde_json::to_value(&any)?;
203        let want = serde_json::json!({
204            "@type": "type.googleapis.com/google.protobuf.Value",
205            "value": true
206        });
207        assert_eq!(got, want);
208        let output = any.to_msg::<Value>()?;
209        assert_eq!(output, input);
210        Ok(())
211    }
212
213    #[test]
214    fn test_number_value() -> Result {
215        let input = serde_json::json!(1234.5);
216        let any = Any::from_msg(&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": 1234.5
221        });
222        assert_eq!(got, want);
223        let output = any.to_msg::<Value>()?;
224        assert_eq!(output, input);
225        Ok(())
226    }
227
228    #[test]
229    fn test_string_value() -> Result {
230        let input = Value::String(String::from("abc123"));
231        let any = Any::from_msg(&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": "abc123"
236        });
237        assert_eq!(got, want);
238        let output = any.to_msg::<Value>()?;
239        assert_eq!(output, input);
240        Ok(())
241    }
242
243    #[test]
244    fn test_struct_in_value() -> Result {
245        let structz = serde_json::json!({
246            "fieldA": "123",
247            "fieldB": {
248                "fieldC": ["a", "b", "c"]
249            }
250        })
251        .as_object()
252        .cloned()
253        .unwrap();
254
255        let input = Value::Object(structz);
256        let any = Any::from_msg(&input)?;
257        let got = serde_json::to_value(&any)?;
258        let want = serde_json::json!({
259            "@type": "type.googleapis.com/google.protobuf.Value",
260            "value": {
261                "fieldA": "123",
262                "fieldB": {
263                    "fieldC": ["a", "b", "c"]
264                }
265            }
266        });
267        assert_eq!(got, want);
268        let output = any.to_msg::<Value>()?;
269        assert_eq!(output, input);
270        Ok(())
271    }
272
273    #[test]
274    fn test_list_value() -> Result {
275        let input = serde_json::json!([1, 2, 3, 4, "abc"])
276            .as_array()
277            .cloned()
278            .unwrap();
279        let any = Any::from_msg(&input)?;
280        let got = serde_json::to_value(&any)?;
281        let want = serde_json::json!({
282            "@type": "type.googleapis.com/google.protobuf.ListValue",
283            "value": [1, 2, 3, 4, "abc"],
284        });
285        assert_eq!(got, want);
286        let output = any.to_msg::<ListValue>()?;
287        assert_eq!(output, input);
288        Ok(())
289    }
290
291    #[test]
292    fn test_struct() -> Result {
293        let input = serde_json::json!({
294            "fieldA": "a_value",
295            "fieldB": {
296                "fieldC": [1, 2, 3, 4, "abc"],
297            },
298        })
299        .as_object()
300        .cloned()
301        .unwrap();
302        let any = Any::from_msg(&input)?;
303        let got = serde_json::to_value(&any)?;
304        let want = serde_json::json!({
305            "@type": "type.googleapis.com/google.protobuf.Struct",
306            "value": {
307                "fieldA": "a_value",
308                "fieldB": {
309                    "fieldC": [1, 2, 3, 4, "abc"],
310                },
311            },
312        });
313        assert_eq!(got, want);
314        let output = any.to_msg::<Struct>()?;
315        assert_eq!(output, input);
316        Ok(())
317    }
318}
319
320#[cfg(test)]
321mod null_value_tests {
322    use super::*;
323    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
324
325    #[test]
326    fn test_wkt_null_value_to_value() {
327        let input = NullValue;
328        let value = Value::from(input);
329        assert!(value.is_null(), "{value:?}");
330
331        let input = &NullValue;
332        let value = Value::from(input);
333        assert!(value.is_null(), "{value:?}");
334    }
335
336    #[test]
337    fn test_i32_from_null_value() {
338        let got = i32::from(NullValue);
339        assert_eq!(got, 0);
340    }
341
342    #[test]
343    fn test_serde_from_null_value() {
344        let got = serde_json::Value::from(NullValue);
345        assert_eq!(got, serde_json::Value::Null);
346        let got = serde_json::Value::from(&NullValue);
347        assert_eq!(got, serde_json::Value::Null);
348    }
349
350    #[test]
351    fn test_null_value_serialize() -> Result {
352        let input = NullValue;
353        let got = serde_json::to_string(&input)?;
354        assert_eq!(got, "null");
355        Ok(())
356    }
357
358    #[test]
359    fn test_null_value_deserialize() -> Result {
360        let input = "null";
361        let got = serde_json::from_str::<NullValue>(input)?;
362        assert_eq!(got, NullValue);
363
364        let input = "123";
365        let got = serde_json::from_str::<NullValue>(input);
366        assert!(got.is_err(), "{got:?}");
367
368        let input = "\"123";
369        let got = serde_json::from_str::<NullValue>(input);
370        assert!(got.is_err(), "{got:?}");
371        Ok(())
372    }
373}