google_cloud_wkt/
wrappers.rs

1// Copyright 2024 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
15use base64::{Engine, engine::general_purpose::STANDARD};
16use serde_with::{DeserializeAs, SerializeAs};
17
18/// Implements the `google.cloud.DoubleValue` well-known type.
19///
20/// In early versions of the `proto3` syntax optional primitive types were
21/// represented by well-known messages, with a single field, that contained the
22/// value. In Rust, we represent these with `Option` of the correct type. The
23/// aliases are introduced here to simplify the code generator and resolve any
24/// references in code or documentation.
25///
26/// The JSON representation for `DoubleValue` is JSON number.
27pub type DoubleValue = f64;
28
29/// Implements the `google.cloud.FloatValue` well-known type.
30///
31/// In early versions of the `proto3` syntax optional primitive types were
32/// represented by well-known messages, with a single field, that contained the
33/// value. In Rust, we represent these with `Option` of the correct type. The
34/// aliases are introduced here to simplify the code generator and resolve any
35/// references in code or documentation.
36///
37/// The JSON representation for `FloatValue` is JSON number.
38pub type FloatValue = f32;
39
40/// Implements the `google.cloud.Int64Value` well-known type.
41///
42/// In early versions of the `proto3` syntax optional primitive types were
43/// represented by well-known messages, with a single field, that contained the
44/// value. In Rust, we represent these with `Option` of the correct type. The
45/// aliases are introduced here to simplify the code generator and resolve any
46/// references in code or documentation.
47///
48/// The JSON representation for `Int64Value` is JSON string.
49pub type Int64Value = i64;
50
51/// Implements the `google.cloud.UInt64Value` well-known type.
52///
53/// In early versions of the `proto3` syntax optional primitive types were
54/// represented by well-known messages, with a single field, that contained the
55/// value. In Rust, we represent these with `Option` of the correct type. The
56/// aliases are introduced here to simplify the code generator and resolve any
57/// references in code or documentation.
58///
59/// The JSON representation for `UInt64Value` is JSON string.
60pub type UInt64Value = u64;
61
62/// Implements the `google.cloud.Int32Value` well-known type.
63///
64/// In early versions of the `proto3` syntax optional primitive types were
65/// represented by well-known messages, with a single field, that contained the
66/// value. In Rust, we represent these with `Option` of the correct type. The
67/// aliases are introduced here to simplify the code generator and resolve any
68/// references in code or documentation.
69///
70/// The JSON representation for `Int32Value` is JSON number.
71pub type Int32Value = i32;
72
73/// Implements the `google.cloud.UInt32Value` well-known type.
74///
75/// In early versions of the `proto3` syntax optional primitive types were
76/// represented by well-known messages, with a single field, that contained the
77/// value. In Rust, we represent these with `Option` of the correct type. The
78/// aliases are introduced here to simplify the code generator and resolve any
79/// references in code or documentation.
80///
81/// The JSON representation for `UInt32Value` is JSON number.
82pub type UInt32Value = u32;
83
84/// Implements the `google.cloud.BoolValue` well-known type.
85///
86/// In early versions of the `proto3` syntax optional primitive types were
87/// represented by well-known messages, with a single field, that contained the
88/// value. In Rust, we represent these with `Option` of the correct type. The
89/// aliases are introduced here to simplify the code generator and resolve any
90/// references in code or documentation.
91///
92/// The JSON representation for `BoolValue` is JSON `true` and `false`.
93pub type BoolValue = bool;
94
95/// Implements the `google.cloud.StringValue` well-known type.
96///
97/// In early versions of the `proto3` syntax optional primitive types were
98/// represented by well-known messages, with a single field, that contained the
99/// value. In Rust, we represent these with `Option` of the correct type. The
100/// aliases are introduced here to simplify the code generator and resolve any
101/// references in code or documentation.
102///
103/// The JSON representation for `StringValue` is JSON string.
104pub type StringValue = String;
105
106/// Implements the `google.cloud.BytesValue` well-known type.
107///
108/// In early versions of the `proto3` syntax optional primitive types were
109/// represented by well-known messages, with a single field, that contained the
110/// value. In Rust, we represent these with `Option` of the correct type. The
111/// aliases are introduced here to simplify the code generator and resolve any
112/// references in code or documentation.
113///
114/// The JSON representation for `BytesValue` is JSON string.
115pub type BytesValue = bytes::Bytes;
116
117macro_rules! impl_message {
118    ($t: ty) => {
119        impl crate::message::Message for $t {
120            fn typename() -> &'static str {
121                concat!("type.googleapis.com/google.protobuf.", stringify!($t))
122            }
123
124            fn serializer() -> impl crate::message::MessageSerializer<Self> {
125                crate::message::ValueSerializer::<Self>::new()
126            }
127        }
128    };
129}
130
131impl_message!(Int32Value);
132impl_message!(UInt32Value);
133impl_message!(BoolValue);
134impl_message!(StringValue);
135
136fn encode_value<T>(value: serde_json::Value) -> Result<crate::message::Map, crate::AnyError>
137where
138    T: crate::message::Message,
139{
140    let map: crate::message::Map = [
141        (
142            "@type",
143            serde_json::Value::String(T::typename().to_string()),
144        ),
145        ("value", value),
146    ]
147    .into_iter()
148    .map(|(k, v)| (k.to_string(), v))
149    .collect();
150    Ok(map)
151}
152
153fn encode_string<T>(value: String) -> Result<crate::message::Map, crate::AnyError>
154where
155    T: crate::message::Message,
156{
157    encode_value::<T>(serde_json::Value::String(value))
158}
159
160impl crate::message::Message for UInt64Value {
161    fn typename() -> &'static str {
162        "type.googleapis.com/google.protobuf.UInt64Value"
163    }
164
165    fn serializer() -> impl crate::message::MessageSerializer<Self> {
166        UInt64ValueSerializer
167    }
168}
169
170struct UInt64ValueSerializer;
171
172#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
173impl crate::message::sealed::MessageSerializer for UInt64ValueSerializer {}
174
175impl crate::message::MessageSerializer<UInt64Value> for UInt64ValueSerializer {
176    fn serialize_to_map(
177        &self,
178        message: &UInt64Value,
179    ) -> Result<crate::message::Map, crate::AnyError> {
180        encode_string::<UInt64Value>(message.to_string())
181    }
182
183    fn deserialize_from_map(
184        &self,
185        map: &crate::message::Map,
186    ) -> Result<UInt64Value, crate::AnyError> {
187        map.get("value")
188            .ok_or_else(crate::message::missing_value_field)?
189            .as_str()
190            .ok_or_else(expected_string_value)?
191            .parse::<UInt64Value>()
192            .map_err(crate::AnyError::deser)
193    }
194}
195
196impl crate::message::Message for Int64Value {
197    fn typename() -> &'static str {
198        "type.googleapis.com/google.protobuf.Int64Value"
199    }
200
201    fn serializer() -> impl crate::message::MessageSerializer<Self>
202    where
203        Self: Sized + serde::ser::Serialize,
204    {
205        Int64ValueSerializer
206    }
207}
208
209struct Int64ValueSerializer;
210
211#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
212impl crate::message::sealed::MessageSerializer for Int64ValueSerializer {}
213
214impl crate::message::MessageSerializer<Int64Value> for Int64ValueSerializer {
215    fn serialize_to_map(
216        &self,
217        message: &Int64Value,
218    ) -> Result<crate::message::Map, crate::AnyError> {
219        encode_string::<Int64Value>(message.to_string())
220    }
221
222    fn deserialize_from_map(
223        &self,
224        map: &crate::message::Map,
225    ) -> Result<Int64Value, crate::AnyError> {
226        map.get("value")
227            .ok_or_else(crate::message::missing_value_field)?
228            .as_str()
229            .ok_or_else(expected_string_value)?
230            .parse::<Int64Value>()
231            .map_err(crate::AnyError::deser)
232    }
233}
234
235impl crate::message::Message for FloatValue {
236    fn typename() -> &'static str {
237        "type.googleapis.com/google.protobuf.FloatValue"
238    }
239
240    fn serializer() -> impl crate::message::MessageSerializer<Self>
241    where
242        Self: Sized + serde::ser::Serialize,
243    {
244        FloatValueSerializer
245    }
246}
247
248struct FloatValueSerializer;
249
250#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
251impl crate::message::sealed::MessageSerializer for FloatValueSerializer {}
252
253impl crate::message::MessageSerializer<FloatValue> for FloatValueSerializer {
254    fn serialize_to_map(
255        &self,
256        message: &FloatValue,
257    ) -> Result<crate::message::Map, crate::AnyError> {
258        let value = crate::internal::F32::serialize_as(message, serde_json::value::Serializer)
259            .map_err(crate::AnyError::ser)?;
260        encode_value::<FloatValue>(value)
261    }
262
263    fn deserialize_from_map(
264        &self,
265        map: &crate::message::Map,
266    ) -> Result<FloatValue, crate::AnyError> {
267        let value = map
268            .get("value")
269            .ok_or_else(crate::message::missing_value_field)?;
270        crate::internal::F32::deserialize_as(value).map_err(crate::AnyError::deser)
271    }
272}
273
274impl crate::message::Message for DoubleValue {
275    fn typename() -> &'static str {
276        "type.googleapis.com/google.protobuf.DoubleValue"
277    }
278
279    fn serializer() -> impl crate::message::MessageSerializer<Self>
280    where
281        Self: Sized + serde::ser::Serialize,
282    {
283        DoubleValueSerializer
284    }
285}
286
287struct DoubleValueSerializer;
288
289#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
290impl crate::message::sealed::MessageSerializer for DoubleValueSerializer {}
291
292impl crate::message::MessageSerializer<DoubleValue> for DoubleValueSerializer {
293    fn serialize_to_map(
294        &self,
295        message: &DoubleValue,
296    ) -> Result<crate::message::Map, crate::AnyError> {
297        let value = crate::internal::F64::serialize_as(message, serde_json::value::Serializer)
298            .map_err(crate::AnyError::ser)?;
299        encode_value::<DoubleValue>(value)
300    }
301
302    fn deserialize_from_map(
303        &self,
304        map: &crate::message::Map,
305    ) -> Result<DoubleValue, crate::AnyError> {
306        let value = map
307            .get("value")
308            .ok_or_else(crate::message::missing_value_field)?;
309        crate::internal::F64::deserialize_as(value).map_err(crate::AnyError::deser)
310    }
311}
312
313impl crate::message::Message for BytesValue {
314    fn typename() -> &'static str {
315        "type.googleapis.com/google.protobuf.BytesValue"
316    }
317
318    fn serializer() -> impl crate::message::MessageSerializer<Self> {
319        BytesValueSerializer
320    }
321}
322
323struct BytesValueSerializer;
324
325#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
326impl crate::message::sealed::MessageSerializer for BytesValueSerializer {}
327
328impl crate::message::MessageSerializer<BytesValue> for BytesValueSerializer {
329    fn serialize_to_map(
330        &self,
331        message: &BytesValue,
332    ) -> Result<crate::message::Map, crate::AnyError> {
333        encode_string::<BytesValue>(STANDARD.encode(message))
334    }
335
336    fn deserialize_from_map(
337        &self,
338        map: &crate::message::Map,
339    ) -> Result<BytesValue, crate::AnyError> {
340        let s = map
341            .get("value")
342            .ok_or_else(crate::message::missing_value_field)?
343            .as_str()
344            .ok_or_else(expected_string_value)?;
345        STANDARD
346            .decode(s)
347            .map(BytesValue::from)
348            .map_err(crate::AnyError::deser)
349    }
350}
351
352fn expected_string_value() -> crate::AnyError {
353    crate::AnyError::deser("expected value field to be a string")
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359    use crate::Any;
360    use crate::message::{Message, MessageSerializer};
361    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
362    use test_case::test_case;
363
364    // Generated with: `echo -n 'Hello, World!' | base64`
365    const HELLO_WORLD_BASE64: &str = "SGVsbG8sIFdvcmxkIQ==";
366
367    #[test_case(1234.5 as DoubleValue, 1234.5, "DoubleValue")]
368    #[test_case(9876.5 as FloatValue, 9876.5, "FloatValue")]
369    #[test_case(-123 as Int64Value, "-123", "Int64Value")]
370    #[test_case(123 as UInt64Value, "123", "UInt64Value")]
371    #[test_case(-123 as Int32Value, -123, "Int32Value")]
372    #[test_case(123 as UInt32Value, 123, "UInt32Value")]
373    #[test_case(true as BoolValue, true, "BoolValue")]
374    #[test_case(StringValue::from("Hello, World!"), "Hello, World!", "StringValue")]
375    #[test_case(BytesValue::from("Hello, World!"), HELLO_WORLD_BASE64, "BytesValue")]
376    fn test_wrapper_in_any<I, V>(input: I, value: V, typename: &str) -> Result
377    where
378        I: crate::message::Message
379            + std::fmt::Debug
380            + PartialEq
381            + serde::de::DeserializeOwned
382            + serde::ser::Serialize,
383        V: serde::ser::Serialize,
384    {
385        let any = Any::from_msg(&input)?;
386        let got = serde_json::to_value(&any)?;
387        let want = serde_json::json!({
388            "@type": format!("type.googleapis.com/google.protobuf.{typename}"),
389            "value": value,
390        });
391        assert_eq!(got, want);
392        let output = any.to_msg::<I>()?;
393        assert_eq!(output, input);
394        Ok(())
395    }
396
397    #[test_case(f32::INFINITY as FloatValue, "Infinity")]
398    #[test_case(f32::NEG_INFINITY as FloatValue, "-Infinity")]
399    #[test_case(f32::NAN as FloatValue, "NaN")]
400    #[test_case(9876.5 as FloatValue, 9876.5)]
401    fn test_wrapper_float<V>(input: FloatValue, value: V) -> Result
402    where
403        V: serde::ser::Serialize,
404    {
405        let any = Any::from_msg(&input)?;
406        let got = serde_json::to_value(&any)?;
407        let want = serde_json::json!({
408            "@type": "type.googleapis.com/google.protobuf.FloatValue",
409            "value": value,
410        });
411        assert_eq!(got, want);
412        let output = any.to_msg::<FloatValue>()?;
413        // Using assert_eq does not work with NaN, as they are not considered equal,
414        // use total_cmp instead.
415        assert!(
416            output.total_cmp(&input) == std::cmp::Ordering::Equal,
417            "expected: {input}, got: {output}"
418        );
419        Ok(())
420    }
421
422    #[test_case(f64::INFINITY as DoubleValue, "Infinity")]
423    #[test_case(f64::NEG_INFINITY as DoubleValue, "-Infinity")]
424    #[test_case(f64::NAN as DoubleValue, "NaN")]
425    #[test_case(9876.5 as DoubleValue, 9876.5)]
426    fn test_wrapper_double<V>(input: DoubleValue, value: V) -> Result
427    where
428        V: serde::ser::Serialize,
429    {
430        let any = Any::from_msg(&input)?;
431        let got = serde_json::to_value(&any)?;
432        let want = serde_json::json!({
433            "@type": "type.googleapis.com/google.protobuf.DoubleValue",
434            "value": value,
435        });
436        assert_eq!(got, want);
437        let output = any.to_msg::<DoubleValue>()?;
438        // Using assert_eq does not work with NaN, as they are not considered equal,
439        // use total_cmp instead.
440        assert!(
441            output.total_cmp(&input) == std::cmp::Ordering::Equal,
442            "expected: {input}, got: {output}"
443        );
444        Ok(())
445    }
446
447    #[test_case(Int32Value::default(), DoubleValue::default())]
448    #[test_case(Int32Value::default(), FloatValue::default())]
449    #[test_case(DoubleValue::default(), Int64Value::default())]
450    #[test_case(DoubleValue::default(), UInt64Value::default())]
451    #[test_case(DoubleValue::default(), Int32Value::default())]
452    #[test_case(DoubleValue::default(), UInt32Value::default())]
453    #[test_case(DoubleValue::default(), BoolValue::default())]
454    #[test_case(DoubleValue::default(), StringValue::default())]
455    #[test_case(DoubleValue::default(), BytesValue::default())]
456    fn test_wrapper_in_any_with_bad_typenames<T, U>(from: T, _into: U) -> Result
457    where
458        T: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
459        U: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
460    {
461        let any = Any::from_msg(&from)?;
462        assert!(any.to_msg::<U>().is_err());
463        Ok(())
464    }
465
466    #[test_case(Int64Value::default(), "Int64Value")]
467    #[test_case(UInt64Value::default(), "UInt64Value")]
468    fn test_wrapper_bad_encoding<T>(_input: T, typename: &str) -> Result
469    where
470        T: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
471    {
472        let map = serde_json::json!({
473            "@type": format!("type.googleapis.com/google.protobuf.{typename}"),
474            "value": 0,
475        });
476
477        let serializer = T::serializer();
478        let e = serializer.deserialize_from_map(map.as_object().unwrap());
479
480        assert!(e.is_err());
481        let fmt = format!("{e:?}");
482        assert!(fmt.contains("expected value field to be a string"), "{fmt}");
483        Ok(())
484    }
485
486    #[test]
487    fn test_wrapper_bad_encoding_base64() -> Result {
488        let map = serde_json::json!({
489            "@type": "type.googleapis.com/google.protobuf.BytesValue",
490            "value": "Oops, I forgot to base64 encode this.",
491        });
492
493        let serializer = BytesValue::serializer();
494        assert!(
495            serializer
496                .deserialize_from_map(map.as_object().unwrap())
497                .is_err()
498        );
499
500        Ok(())
501    }
502}