aboutsummaryrefslogtreecommitdiff
path: root/src/any.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/any.rs')
-rw-r--r--src/any.rs114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/any.rs b/src/any.rs
new file mode 100644
index 0000000..31ddd82
--- /dev/null
+++ b/src/any.rs
@@ -0,0 +1,114 @@
+use crate::parse_from_bytes;
+use crate::reflect::MessageDescriptor;
+use crate::well_known_types::Any;
+use crate::Message;
+use crate::ProtobufResult;
+
+impl Any {
+ fn type_url(type_url_prefix: &str, descriptor: &MessageDescriptor) -> String {
+ format!("{}/{}", type_url_prefix, descriptor.full_name())
+ }
+
+ fn get_type_name_from_type_url(type_url: &str) -> Option<&str> {
+ match type_url.rfind('/') {
+ Some(i) => Some(&type_url[i + 1..]),
+ None => None,
+ }
+ }
+
+ /// Pack any message into `well_known_types::Any` value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use protobuf::Message;
+ /// # use protobuf::ProtobufResult;
+ /// use protobuf::well_known_types::Any;
+ ///
+ /// # fn the_test<MyMessage: Message>(message: &MyMessage) -> ProtobufResult<()> {
+ /// let message: &MyMessage = message;
+ /// let any = Any::pack(message)?;
+ /// assert!(any.is::<MyMessage>());
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn pack<M: Message>(message: &M) -> ProtobufResult<Any> {
+ Any::pack_dyn(message)
+ }
+
+ /// Pack any message into `well_known_types::Any` value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use protobuf::Message;
+ /// # use protobuf::ProtobufResult;
+ /// use protobuf::well_known_types::Any;
+ ///
+ /// # fn the_test(message: &dyn Message) -> ProtobufResult<()> {
+ /// let message: &dyn Message = message;
+ /// let any = Any::pack_dyn(message)?;
+ /// assert!(any.is_dyn(message.descriptor()));
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn pack_dyn(message: &dyn Message) -> ProtobufResult<Any> {
+ Any::pack_with_type_url_prefix(message, "type.googleapis.com")
+ }
+
+ fn pack_with_type_url_prefix(
+ message: &dyn Message,
+ type_url_prefix: &str,
+ ) -> ProtobufResult<Any> {
+ Ok(Any {
+ type_url: Any::type_url(type_url_prefix, message.descriptor()),
+ value: message.write_to_bytes()?,
+ ..Default::default()
+ })
+ }
+
+ /// Check if `Any` contains a message of given type.
+ pub fn is<M: Message>(&self) -> bool {
+ self.is_dyn(M::descriptor_static())
+ }
+
+ /// Check if `Any` contains a message of given type.
+ pub fn is_dyn(&self, descriptor: &MessageDescriptor) -> bool {
+ match Any::get_type_name_from_type_url(&self.type_url) {
+ Some(type_name) => type_name == descriptor.full_name(),
+ None => false,
+ }
+ }
+
+ /// Extract a message from this `Any`.
+ ///
+ /// # Returns
+ ///
+ /// * `Ok(None)` when message type mismatch
+ /// * `Err` when parse failed
+ pub fn unpack<M: Message>(&self) -> ProtobufResult<Option<M>> {
+ if !self.is::<M>() {
+ return Ok(None);
+ }
+ Ok(Some(parse_from_bytes(&self.value)?))
+ }
+
+ /// Extract a message from this `Any`.
+ ///
+ /// # Returns
+ ///
+ /// * `Ok(None)` when message type mismatch
+ /// * `Err` when parse failed
+ pub fn unpack_dyn(
+ &self,
+ descriptor: &MessageDescriptor,
+ ) -> ProtobufResult<Option<Box<dyn Message>>> {
+ if !self.is_dyn(descriptor) {
+ return Ok(None);
+ }
+ let mut message = descriptor.new_instance();
+ message.merge_from_bytes(&self.value)?;
+ message.check_initialized()?;
+ Ok(Some(message))
+ }
+}