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            #[allow(private_interfaces)]
125            fn serializer() -> impl crate::message::MessageSerializer<Self> {
126                crate::message::ValueSerializer::<Self>::new()
127            }
128        }
129    };
130}
131
132impl_message!(Int32Value);
133impl_message!(UInt32Value);
134impl_message!(BoolValue);
135impl_message!(StringValue);
136
137fn encode_value<T>(value: serde_json::Value) -> Result<crate::message::Map, crate::AnyError>
138where
139    T: crate::message::Message,
140{
141    let map: crate::message::Map = [
142        (
143            "@type",
144            serde_json::Value::String(T::typename().to_string()),
145        ),
146        ("value", value),
147    ]
148    .into_iter()
149    .map(|(k, v)| (k.to_string(), v))
150    .collect();
151    Ok(map)
152}
153
154fn encode_string<T>(value: String) -> Result<crate::message::Map, crate::AnyError>
155where
156    T: crate::message::Message,
157{
158    encode_value::<T>(serde_json::Value::String(value))
159}
160
161impl crate::message::Message for UInt64Value {
162    fn typename() -> &'static str {
163        "type.googleapis.com/google.protobuf.UInt64Value"
164    }
165
166    #[allow(private_interfaces)]
167    fn serializer() -> impl crate::message::MessageSerializer<Self> {
168        UInt64ValueSerializer
169    }
170}
171
172struct UInt64ValueSerializer;
173
174impl crate::message::MessageSerializer<UInt64Value> for UInt64ValueSerializer {
175    fn serialize_to_map(
176        &self,
177        message: &UInt64Value,
178    ) -> Result<crate::message::Map, crate::AnyError> {
179        encode_string::<UInt64Value>(message.to_string())
180    }
181
182    fn deserialize_from_map(
183        &self,
184        map: &crate::message::Map,
185    ) -> Result<UInt64Value, crate::AnyError> {
186        map.get("value")
187            .ok_or_else(crate::message::missing_value_field)?
188            .as_str()
189            .ok_or_else(expected_string_value)?
190            .parse::<UInt64Value>()
191            .map_err(crate::AnyError::deser)
192    }
193}
194
195impl crate::message::Message for Int64Value {
196    fn typename() -> &'static str {
197        "type.googleapis.com/google.protobuf.Int64Value"
198    }
199
200    #[allow(private_interfaces)]
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
211impl crate::message::MessageSerializer<Int64Value> for Int64ValueSerializer {
212    fn serialize_to_map(
213        &self,
214        message: &Int64Value,
215    ) -> Result<crate::message::Map, crate::AnyError> {
216        encode_string::<Int64Value>(message.to_string())
217    }
218
219    fn deserialize_from_map(
220        &self,
221        map: &crate::message::Map,
222    ) -> Result<Int64Value, crate::AnyError> {
223        map.get("value")
224            .ok_or_else(crate::message::missing_value_field)?
225            .as_str()
226            .ok_or_else(expected_string_value)?
227            .parse::<Int64Value>()
228            .map_err(crate::AnyError::deser)
229    }
230}
231
232impl crate::message::Message for FloatValue {
233    fn typename() -> &'static str {
234        "type.googleapis.com/google.protobuf.FloatValue"
235    }
236
237    #[allow(private_interfaces)]
238    fn serializer() -> impl crate::message::MessageSerializer<Self>
239    where
240        Self: Sized + serde::ser::Serialize,
241    {
242        FloatValueSerializer
243    }
244}
245
246struct FloatValueSerializer;
247
248impl crate::message::MessageSerializer<FloatValue> for FloatValueSerializer {
249    fn serialize_to_map(
250        &self,
251        message: &FloatValue,
252    ) -> Result<crate::message::Map, crate::AnyError> {
253        let value = crate::internal::F32::serialize_as(message, serde_json::value::Serializer)
254            .map_err(crate::AnyError::ser)?;
255        encode_value::<FloatValue>(value)
256    }
257
258    fn deserialize_from_map(
259        &self,
260        map: &crate::message::Map,
261    ) -> Result<FloatValue, crate::AnyError> {
262        let value = map
263            .get("value")
264            .ok_or_else(crate::message::missing_value_field)?;
265        crate::internal::F32::deserialize_as(value).map_err(crate::AnyError::deser)
266    }
267}
268
269impl crate::message::Message for DoubleValue {
270    fn typename() -> &'static str {
271        "type.googleapis.com/google.protobuf.DoubleValue"
272    }
273
274    #[allow(private_interfaces)]
275    fn serializer() -> impl crate::message::MessageSerializer<Self>
276    where
277        Self: Sized + serde::ser::Serialize,
278    {
279        DoubleValueSerializer
280    }
281}
282
283struct DoubleValueSerializer;
284
285impl crate::message::MessageSerializer<DoubleValue> for DoubleValueSerializer {
286    fn serialize_to_map(
287        &self,
288        message: &DoubleValue,
289    ) -> Result<crate::message::Map, crate::AnyError> {
290        let value = crate::internal::F64::serialize_as(message, serde_json::value::Serializer)
291            .map_err(crate::AnyError::ser)?;
292        encode_value::<DoubleValue>(value)
293    }
294
295    fn deserialize_from_map(
296        &self,
297        map: &crate::message::Map,
298    ) -> Result<DoubleValue, crate::AnyError> {
299        let value = map
300            .get("value")
301            .ok_or_else(crate::message::missing_value_field)?;
302        crate::internal::F64::deserialize_as(value).map_err(crate::AnyError::deser)
303    }
304}
305
306impl crate::message::Message for BytesValue {
307    fn typename() -> &'static str {
308        "type.googleapis.com/google.protobuf.BytesValue"
309    }
310
311    #[allow(private_interfaces)]
312    fn serializer() -> impl crate::message::MessageSerializer<Self> {
313        BytesValueSerializer
314    }
315}
316
317struct BytesValueSerializer;
318
319impl crate::message::MessageSerializer<BytesValue> for BytesValueSerializer {
320    fn serialize_to_map(
321        &self,
322        message: &BytesValue,
323    ) -> Result<crate::message::Map, crate::AnyError> {
324        encode_string::<BytesValue>(STANDARD.encode(message))
325    }
326
327    fn deserialize_from_map(
328        &self,
329        map: &crate::message::Map,
330    ) -> Result<BytesValue, crate::AnyError> {
331        let s = map
332            .get("value")
333            .ok_or_else(crate::message::missing_value_field)?
334            .as_str()
335            .ok_or_else(expected_string_value)?;
336        STANDARD
337            .decode(s)
338            .map(BytesValue::from)
339            .map_err(crate::AnyError::deser)
340    }
341}
342
343fn expected_string_value() -> crate::AnyError {
344    crate::AnyError::deser("expected value field to be a string")
345}
346
347#[cfg(test)]
348mod tests {
349    use super::*;
350    use crate::Any;
351    use crate::message::{Message, MessageSerializer};
352    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
353    use test_case::test_case;
354
355    // Generated with: `echo -n 'Hello, World!' | base64`
356    const HELLO_WORLD_BASE64: &str = "SGVsbG8sIFdvcmxkIQ==";
357
358    #[test_case(1234.5 as DoubleValue, 1234.5, "DoubleValue")]
359    #[test_case(9876.5 as FloatValue, 9876.5, "FloatValue")]
360    #[test_case(-123 as Int64Value, "-123", "Int64Value")]
361    #[test_case(123 as UInt64Value, "123", "UInt64Value")]
362    #[test_case(-123 as Int32Value, -123, "Int32Value")]
363    #[test_case(123 as UInt32Value, 123, "UInt32Value")]
364    #[test_case(true as BoolValue, true, "BoolValue")]
365    #[test_case(StringValue::from("Hello, World!"), "Hello, World!", "StringValue")]
366    #[test_case(BytesValue::from("Hello, World!"), HELLO_WORLD_BASE64, "BytesValue")]
367    fn test_wrapper_in_any<I, V>(input: I, value: V, typename: &str) -> Result
368    where
369        I: crate::message::Message
370            + std::fmt::Debug
371            + PartialEq
372            + serde::de::DeserializeOwned
373            + serde::ser::Serialize,
374        V: serde::ser::Serialize,
375    {
376        let any = Any::from_msg(&input)?;
377        let got = serde_json::to_value(&any)?;
378        let want = serde_json::json!({
379            "@type": format!("type.googleapis.com/google.protobuf.{typename}"),
380            "value": value,
381        });
382        assert_eq!(got, want);
383        let output = any.to_msg::<I>()?;
384        assert_eq!(output, input);
385        Ok(())
386    }
387
388    #[test_case(f32::INFINITY as FloatValue, "Infinity")]
389    #[test_case(f32::NEG_INFINITY as FloatValue, "-Infinity")]
390    #[test_case(f32::NAN as FloatValue, "NaN")]
391    #[test_case(9876.5 as FloatValue, 9876.5)]
392    fn test_wrapper_float<V>(input: FloatValue, value: V) -> Result
393    where
394        V: serde::ser::Serialize,
395    {
396        let any = Any::from_msg(&input)?;
397        let got = serde_json::to_value(&any)?;
398        let want = serde_json::json!({
399            "@type": "type.googleapis.com/google.protobuf.FloatValue",
400            "value": value,
401        });
402        assert_eq!(got, want);
403        let output = any.to_msg::<FloatValue>()?;
404        // Using assert_eq does not work with NaN, as they are not considered equal,
405        // use total_cmp instead.
406        assert!(
407            output.total_cmp(&input) == std::cmp::Ordering::Equal,
408            "expected: {input}, got: {output}"
409        );
410        Ok(())
411    }
412
413    #[test_case(f64::INFINITY as DoubleValue, "Infinity")]
414    #[test_case(f64::NEG_INFINITY as DoubleValue, "-Infinity")]
415    #[test_case(f64::NAN as DoubleValue, "NaN")]
416    #[test_case(9876.5 as DoubleValue, 9876.5)]
417    fn test_wrapper_double<V>(input: DoubleValue, value: V) -> Result
418    where
419        V: serde::ser::Serialize,
420    {
421        let any = Any::from_msg(&input)?;
422        let got = serde_json::to_value(&any)?;
423        let want = serde_json::json!({
424            "@type": "type.googleapis.com/google.protobuf.DoubleValue",
425            "value": value,
426        });
427        assert_eq!(got, want);
428        let output = any.to_msg::<DoubleValue>()?;
429        // Using assert_eq does not work with NaN, as they are not considered equal,
430        // use total_cmp instead.
431        assert!(
432            output.total_cmp(&input) == std::cmp::Ordering::Equal,
433            "expected: {input}, got: {output}"
434        );
435        Ok(())
436    }
437
438    #[test_case(Int32Value::default(), DoubleValue::default())]
439    #[test_case(Int32Value::default(), FloatValue::default())]
440    #[test_case(DoubleValue::default(), Int64Value::default())]
441    #[test_case(DoubleValue::default(), UInt64Value::default())]
442    #[test_case(DoubleValue::default(), Int32Value::default())]
443    #[test_case(DoubleValue::default(), UInt32Value::default())]
444    #[test_case(DoubleValue::default(), BoolValue::default())]
445    #[test_case(DoubleValue::default(), StringValue::default())]
446    #[test_case(DoubleValue::default(), BytesValue::default())]
447    fn test_wrapper_in_any_with_bad_typenames<T, U>(from: T, _into: U) -> Result
448    where
449        T: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
450        U: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
451    {
452        let any = Any::from_msg(&from)?;
453        assert!(any.to_msg::<U>().is_err());
454        Ok(())
455    }
456
457    #[test_case(Int64Value::default(), "Int64Value")]
458    #[test_case(UInt64Value::default(), "UInt64Value")]
459    fn test_wrapper_bad_encoding<T>(_input: T, typename: &str) -> Result
460    where
461        T: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
462    {
463        let map = serde_json::json!({
464            "@type": format!("type.googleapis.com/google.protobuf.{typename}"),
465            "value": 0,
466        });
467
468        let serializer = T::serializer();
469        let e = serializer.deserialize_from_map(map.as_object().unwrap());
470
471        assert!(e.is_err());
472        let fmt = format!("{e:?}");
473        assert!(fmt.contains("expected value field to be a string"), "{fmt}");
474        Ok(())
475    }
476
477    #[test]
478    fn test_wrapper_bad_encoding_base64() -> Result {
479        let map = serde_json::json!({
480            "@type": "type.googleapis.com/google.protobuf.BytesValue",
481            "value": "Oops, I forgot to base64 encode this.",
482        });
483
484        let serializer = BytesValue::serializer();
485        assert!(
486            serializer
487                .deserialize_from_map(map.as_object().unwrap())
488                .is_err()
489        );
490
491        Ok(())
492    }
493}