From 67ec6da80a0ddd9bb5c73619255ca823dc12b45c Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 16 May 2023 19:07:23 -0600 Subject: [PATCH] feat: `TypeUrl` trait + `Any` encoding support As discussed in #299, adds a `TypeUrl` trait which associates a type URL constant with a `Message` type. This enables adding methods to `Any` for decoding/encoding messages: - `Any::from_message`: encodes a given `Message`, returning `Any`. - `Any::to_message`: decodes `Any::value` as the given `Message`, first validating the message type has the expected type URL. --- prost-types/src/lib.rs | 36 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/type_url.rs | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/type_url.rs diff --git a/prost-types/src/lib.rs b/prost-types/src/lib.rs index 7d60f885c..855b89524 100644 --- a/prost-types/src/lib.rs +++ b/prost-types/src/lib.rs @@ -24,6 +24,10 @@ use core::i64; use core::str::FromStr; use core::time; +use prost::alloc::borrow::ToOwned; +use prost::alloc::format; +use prost::{DecodeError, EncodeError, Message, TypeUrl}; + pub use protobuf::*; // The Protobuf `Duration` and `Timestamp` types can't delegate to the standard library equivalents @@ -33,6 +37,38 @@ pub use protobuf::*; const NANOS_PER_SECOND: i32 = 1_000_000_000; const NANOS_MAX: i32 = NANOS_PER_SECOND - 1; +impl Any { + /// Serialize this message proto as [`Any`]. + pub fn from_message(msg: &M) -> Result + where + M: TypeUrl, + { + let type_url = M::TYPE_URL.to_owned(); + let mut value = Vec::new(); + Message::encode(msg, &mut value)?; + Ok(Any { type_url, value }) + } + + /// Decode the given message type `M` from [`Any`], validating that it has + /// the expected [`TypeUrl`]. + pub fn to_message(&self) -> Result + where + M: Default + Sized + TypeUrl, + { + if self.type_url == M::TYPE_URL { + Ok(M::decode(&*self.value)?) + } else { + let mut err = DecodeError::new(format!( + "expected type URL: \"{}\" (got: \"{}\")", + M::TYPE_URL, + &self.type_url + )); + err.push("unexpected type URL", "type_url"); + Err(err) + } + } +} + impl Duration { /// Normalizes the duration to a canonical format. /// diff --git a/src/lib.rs b/src/lib.rs index f188f75c9..eb9cff6eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub use bytes; mod error; mod message; +mod type_url; mod types; #[doc(hidden)] @@ -18,6 +19,7 @@ pub mod encoding; pub use crate::error::{DecodeError, EncodeError}; pub use crate::message::Message; +pub use crate::type_url::TypeUrl; use bytes::{Buf, BufMut}; diff --git a/src/type_url.rs b/src/type_url.rs new file mode 100644 index 000000000..bf3c2d225 --- /dev/null +++ b/src/type_url.rs @@ -0,0 +1,19 @@ +//! Support for associating a type URL with a [`Message`]. + +use crate::Message; + +/// Associate a type URL with the given [`Message]`. +pub trait TypeUrl: Message { + /// Type URL for this [`Message`]. They take the form: + /// + /// ```text + /// /. + /// ``` + /// + /// For example: + /// + /// ```text + /// /foo.bar.baz.MyTypeName + /// ``` + const TYPE_URL: &'static str; +}