tendermint_proto/google/protobuf/
any.rs

1// Original code from <https://github.com/influxdata/pbjson/blob/main/pbjson-types/src/any.rs>
2// Copyright 2022 Dan Burkert & Tokio Contributors
3
4use prost::{DecodeError, EncodeError, Message, Name};
5use subtle_encoding::base64;
6
7use crate::prelude::*;
8
9use super::type_url::{type_url_for, TypeUrl};
10use super::PACKAGE;
11
12/// `Any` contains an arbitrary serialized protocol buffer message along with a
13/// URL that describes the type of the serialized message.
14///
15/// Protobuf library provides support to pack/unpack Any values in the form
16/// of utility functions or additional generated methods of the Any type.
17///
18/// # Example
19///
20/// Pack and unpack a message in Rust:
21///
22/// ```rust,ignore
23/// let foo1 = Foo { ... };
24/// let any = Any::from_msg(&foo1)?;
25/// let foo2 = any.to_msg::<Foo>()?;
26/// assert_eq!(foo1, foo2);
27/// ```
28///
29/// The pack methods provided by protobuf library will by default use
30/// 'type.googleapis.com/full.type.name' as the type URL and the unpack
31/// methods only use the fully qualified type name after the last '/'
32/// in the type URL, for example "foo.bar.com/x/y.z" will yield type
33/// name "y.z".
34///
35/// # JSON
36///
37/// JSON serialization of Any cannot be made compatible with the specification.
38/// See <https://github.com/influxdata/pbjson/issues/2> for more information.
39///
40/// At the moment, an `Any` struct will be serialized as a JSON object with two fields:
41/// - `typeUrl` (string): the type URL of the message
42/// - `value` (string): the base64-encoded serialized message
43///
44/// For example:
45/// ```json
46/// {
47///    "typeUrl": "type.googleapis.com/google.protobuf.Duration",
48///    "value": "Cg0KB2NvcnA="
49/// }
50/// ```
51#[derive(Clone, PartialEq, Eq, ::prost::Message)]
52#[cfg_attr(feature = "json-schema", derive(::schemars::JsonSchema))]
53pub struct Any {
54    /// A URL/resource name that uniquely identifies the type of the serialized
55    /// protocol buffer message. This string must contain at least
56    /// one "/" character. The last segment of the URL's path must represent
57    /// the fully qualified name of the type (as in
58    /// `path/google.protobuf.Duration`). The name should be in a canonical form
59    /// (e.g., leading "." is not accepted).
60    ///
61    /// In practice, teams usually precompile into the binary all types that they
62    /// expect it to use in the context of Any. However, for URLs which use the
63    /// scheme `http`, `https`, or no scheme, one can optionally set up a type
64    /// server that maps type URLs to message definitions as follows:
65    ///
66    /// * If no scheme is provided, `https` is assumed.
67    /// * An HTTP GET on the URL must yield a \[google.protobuf.Type\]\[\]
68    ///   value in binary format, or produce an error.
69    /// * Applications are allowed to cache lookup results based on the
70    ///   URL, or have them precompiled into a binary to avoid any
71    ///   lookup. Therefore, binary compatibility needs to be preserved
72    ///   on changes to types. (Use versioned type names to manage
73    ///   breaking changes.)
74    ///
75    /// Note: this functionality is not currently available in the official
76    /// protobuf release, and it is not used for type URLs beginning with
77    /// type.googleapis.com.
78    ///
79    /// Schemes other than `http`, `https` (or the empty scheme) might be
80    /// used with implementation specific semantics.
81    #[prost(string, tag = "1")]
82    pub type_url: ::prost::alloc::string::String,
83    /// Must be a valid serialized protocol buffer of the above specified type.
84    #[prost(bytes = "vec", tag = "2")]
85    pub value: ::prost::alloc::vec::Vec<u8>,
86}
87
88impl Any {
89    /// Serialize the given message type `M` as [`Any`].
90    pub fn from_msg<M>(msg: &M) -> Result<Self, EncodeError>
91    where
92        M: Name,
93    {
94        let type_url = M::type_url();
95        let mut value = Vec::new();
96        Message::encode(msg, &mut value)?;
97        Ok(Any { type_url, value })
98    }
99
100    /// Decode the given message type `M` from [`Any`], validating that it has
101    /// the expected type URL.
102    pub fn to_msg<M>(&self) -> Result<M, DecodeError>
103    where
104        M: Default + Name + Sized,
105    {
106        let expected_type_url = M::type_url();
107
108        if let (Some(expected), Some(actual)) = (
109            TypeUrl::new(&expected_type_url),
110            TypeUrl::new(&self.type_url),
111        ) {
112            if expected == actual {
113                return M::decode(self.value.as_slice());
114            }
115        }
116
117        let mut err = DecodeError::new(format!(
118            "expected type URL: \"{}\" (got: \"{}\")",
119            expected_type_url, &self.type_url
120        ));
121        err.push("unexpected type URL", "type_url");
122        Err(err)
123    }
124}
125
126impl Name for Any {
127    const PACKAGE: &'static str = PACKAGE;
128    const NAME: &'static str = "Any";
129
130    fn type_url() -> String {
131        type_url_for::<Self>()
132    }
133}
134
135impl serde::Serialize for Any {
136    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
137    where
138        S: serde::Serializer,
139    {
140        use serde::ser::SerializeStruct;
141        let mut len = 0;
142        if !self.type_url.is_empty() {
143            len += 1;
144        }
145        if !self.value.is_empty() {
146            len += 1;
147        }
148        let mut struct_ser = serializer.serialize_struct("google.protobuf.Any", len)?;
149        if !self.type_url.is_empty() {
150            struct_ser.serialize_field("typeUrl", &self.type_url)?;
151        }
152        if !self.value.is_empty() {
153            // NOTE: A base64 string is always valid UTF-8.
154            struct_ser.serialize_field(
155                "value",
156                &String::from_utf8_lossy(&base64::encode(&self.value)),
157            )?;
158        }
159        struct_ser.end()
160    }
161}
162impl<'de> serde::Deserialize<'de> for Any {
163    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
164    where
165        D: serde::Deserializer<'de>,
166    {
167        const FIELDS: &[&str] = &["type_url", "typeUrl", "value"];
168
169        #[allow(clippy::enum_variant_names)]
170        enum GeneratedField {
171            TypeUrl,
172            Value,
173        }
174        impl<'de> serde::Deserialize<'de> for GeneratedField {
175            fn deserialize<D>(deserializer: D) -> core::result::Result<GeneratedField, D::Error>
176            where
177                D: serde::Deserializer<'de>,
178            {
179                struct GeneratedVisitor;
180
181                impl serde::de::Visitor<'_> for GeneratedVisitor {
182                    type Value = GeneratedField;
183
184                    fn expecting(
185                        &self,
186                        formatter: &mut core::fmt::Formatter<'_>,
187                    ) -> core::fmt::Result {
188                        write!(formatter, "expected one of: {:?}", &FIELDS)
189                    }
190
191                    #[allow(unused_variables)]
192                    fn visit_str<E>(self, value: &str) -> core::result::Result<GeneratedField, E>
193                    where
194                        E: serde::de::Error,
195                    {
196                        match value {
197                            "typeUrl" | "type_url" => Ok(GeneratedField::TypeUrl),
198                            "value" => Ok(GeneratedField::Value),
199                            _ => Err(serde::de::Error::unknown_field(value, FIELDS)),
200                        }
201                    }
202                }
203                deserializer.deserialize_identifier(GeneratedVisitor)
204            }
205        }
206        struct GeneratedVisitor;
207        impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
208            type Value = Any;
209
210            fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211                formatter.write_str("struct google.protobuf.Any")
212            }
213
214            fn visit_map<V>(self, mut map_: V) -> core::result::Result<Any, V::Error>
215            where
216                V: serde::de::MapAccess<'de>,
217            {
218                let mut type_url__ = None;
219                let mut value__ = None;
220                while let Some(k) = map_.next_key()? {
221                    match k {
222                        GeneratedField::TypeUrl => {
223                            if type_url__.is_some() {
224                                return Err(serde::de::Error::duplicate_field("typeUrl"));
225                            }
226                            type_url__ = Some(map_.next_value()?);
227                        },
228                        GeneratedField::Value => {
229                            if value__.is_some() {
230                                return Err(serde::de::Error::duplicate_field("value"));
231                            }
232                            let b64_str = map_.next_value::<String>()?;
233                            let value = base64::decode(b64_str.as_bytes()).map_err(|e| {
234                                serde::de::Error::custom(format!("base64 decode error: {e}"))
235                            })?;
236                            value__ = Some(value);
237                        },
238                    }
239                }
240                Ok(Any {
241                    type_url: type_url__.unwrap_or_default(),
242                    value: value__.unwrap_or_default(),
243                })
244            }
245        }
246        deserializer.deserialize_struct("google.protobuf.Any", FIELDS, GeneratedVisitor)
247    }
248}
249
250#[cfg(any(feature = "borsh", feature = "parity-scale-codec"))]
251mod sealed {
252    use super::Any;
253
254    use alloc::string::String;
255    use alloc::vec::Vec;
256
257    #[cfg_attr(
258        feature = "parity-scale-codec",
259        derive(
260            parity_scale_codec::Encode,
261            parity_scale_codec::Decode,
262            scale_info::TypeInfo
263        )
264    )]
265    #[cfg_attr(
266        feature = "borsh",
267        derive(borsh::BorshSerialize, borsh::BorshDeserialize)
268    )]
269    struct InnerAny {
270        pub type_url: String,
271        pub value: Vec<u8>,
272    }
273
274    #[cfg(feature = "borsh")]
275    impl borsh::BorshSerialize for Any {
276        fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
277            let inner_any = InnerAny {
278                type_url: self.type_url.clone(),
279                value: self.value.clone(),
280            };
281
282            borsh::BorshSerialize::serialize(&inner_any, writer)
283        }
284    }
285
286    #[cfg(feature = "borsh")]
287    impl borsh::BorshDeserialize for Any {
288        fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
289            let inner_any = InnerAny::deserialize_reader(reader)?;
290
291            Ok(Any {
292                type_url: inner_any.type_url,
293                value: inner_any.value,
294            })
295        }
296    }
297
298    #[cfg(feature = "parity-scale-codec")]
299    impl parity_scale_codec::Encode for Any {
300        fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, writer: &mut T) {
301            let inner_any = InnerAny {
302                type_url: self.type_url.clone(),
303                value: self.value.clone(),
304            };
305            inner_any.encode_to(writer);
306        }
307    }
308    #[cfg(feature = "parity-scale-codec")]
309    impl parity_scale_codec::Decode for Any {
310        fn decode<I: parity_scale_codec::Input>(
311            input: &mut I,
312        ) -> Result<Self, parity_scale_codec::Error> {
313            let inner_any = InnerAny::decode(input)?;
314            Ok(Any {
315                type_url: inner_any.type_url.clone(),
316                value: inner_any.value,
317            })
318        }
319    }
320
321    #[cfg(feature = "parity-scale-codec")]
322    impl scale_info::TypeInfo for Any {
323        type Identity = Self;
324
325        fn type_info() -> scale_info::Type {
326            scale_info::Type::builder()
327                .path(scale_info::Path::new("Any", "ibc_proto::google::protobuf"))
328                // i128 is chosen before we represent the timestamp is nanoseconds, which is represented as a i128 by Time
329                .composite(
330                    scale_info::build::Fields::named()
331                        .field(|f| f.ty::<String>().name("type_url").type_name("String"))
332                        .field(|f| f.ty::<Vec<u8>>().name("value").type_name("Vec<u8>")),
333                )
334        }
335    }
336}