Skip to content

Commit e91bf7e

Browse files
committed
Implement Any::pack,is,unpack operations
1 parent 8c933da commit e91bf7e

4 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use super::test_any_pb::MessageOne;
2+
use super::test_any_pb::MessageTwo;
3+
use protobuf::well_known_types::Any;
4+
use protobuf::Message;
5+
6+
#[test]
7+
fn test_static() {
8+
let mut m1 = MessageOne::new();
9+
m1.set_i(10);
10+
let any = Any::pack(&m1).unwrap();
11+
assert_eq!("type.googleapis.com/test_any.MessageOne", any.type_url);
12+
assert!(any.is::<MessageOne>());
13+
assert!(!any.is::<MessageTwo>());
14+
assert_eq!(Some(m1), any.unpack::<MessageOne>().unwrap());
15+
assert_eq!(None, any.unpack::<MessageTwo>().unwrap());
16+
}
17+
18+
#[test]
19+
fn test_dynamic() {
20+
let mut m1 = MessageOne::new();
21+
m1.set_i(10);
22+
let any = Any::pack_dyn(&m1).unwrap();
23+
assert_eq!("type.googleapis.com/test_any.MessageOne", any.type_url);
24+
assert!(any.is_dyn(MessageOne::descriptor_static()));
25+
assert!(!any.is_dyn(MessageTwo::descriptor_static()));
26+
assert_eq!(
27+
m1,
28+
*any.unpack_dyn(MessageOne::descriptor_static())
29+
.unwrap()
30+
.unwrap()
31+
.downcast_box::<MessageOne>()
32+
.unwrap()
33+
);
34+
assert!(any
35+
.unpack_dyn(MessageTwo::descriptor_static())
36+
.unwrap()
37+
.is_none());
38+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
syntax = "proto2";
2+
3+
import "rustproto.proto";
4+
option (rustproto.generate_accessors_all) = true;
5+
6+
package test_any;
7+
8+
message MessageOne {
9+
optional int32 i = 1;
10+
}
11+
12+
message MessageTwo {
13+
14+
}

protobuf/src/any.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use crate::parse_from_bytes;
2+
use crate::reflect::MessageDescriptor;
3+
use crate::well_known_types::Any;
4+
use crate::Message;
5+
use crate::ProtobufResult;
6+
7+
impl Any {
8+
fn get_type_url(type_url_prefix: &str, descriptor: &MessageDescriptor) -> String {
9+
format!("{}/{}", type_url_prefix, descriptor.full_name())
10+
}
11+
12+
fn get_type_name_from_type_url(type_url: &str) -> Option<&str> {
13+
match type_url.rfind('/') {
14+
Some(i) => Some(&type_url[i + 1..]),
15+
None => None,
16+
}
17+
}
18+
19+
/// Pack any message into `well_known_types::Any` value.
20+
///
21+
/// # Examples
22+
///
23+
/// ```
24+
/// # use protobuf::Message;
25+
/// # use protobuf::ProtobufResult;
26+
/// use protobuf::well_known_types::Any;
27+
///
28+
/// # fn the_test<MyMessage: Message>(message: &MyMessage) -> ProtobufResult<()> {
29+
/// let message: &MyMessage = message;
30+
/// let any = Any::pack(message)?;
31+
/// assert!(any.is::<MyMessage>());
32+
/// # Ok(())
33+
/// # }
34+
/// ```
35+
pub fn pack<M: Message>(message: &M) -> ProtobufResult<Any> {
36+
Any::pack_dyn(message)
37+
}
38+
39+
/// Pack any message into `well_known_types::Any` value.
40+
///
41+
/// # Examples
42+
///
43+
/// ```
44+
/// use protobuf::Message;
45+
/// # use protobuf::ProtobufResult;
46+
/// use protobuf::well_known_types::Any;
47+
///
48+
/// # fn the_test(message: &dyn Message) -> ProtobufResult<()> {
49+
/// let message: &dyn Message = message;
50+
/// let any = Any::pack_dyn(message)?;
51+
/// assert!(any.is_dyn(message.descriptor()));
52+
/// # Ok(())
53+
/// # }
54+
/// ```
55+
pub fn pack_dyn(message: &dyn Message) -> ProtobufResult<Any> {
56+
Any::pack_with_type_url_prefix(message, "type.googleapis.com")
57+
}
58+
59+
fn pack_with_type_url_prefix(
60+
message: &dyn Message,
61+
type_url_prefix: &str,
62+
) -> ProtobufResult<Any> {
63+
Ok(Any {
64+
type_url: Any::get_type_url(type_url_prefix, message.descriptor()),
65+
value: message.write_to_bytes()?,
66+
..Default::default()
67+
})
68+
}
69+
70+
/// Check if `Any` contains a message of given type.
71+
pub fn is<M: Message>(&self) -> bool {
72+
self.is_dyn(M::descriptor_static())
73+
}
74+
75+
/// Check if `Any` contains a message of given type.
76+
pub fn is_dyn(&self, descriptor: &MessageDescriptor) -> bool {
77+
match Any::get_type_name_from_type_url(&self.type_url) {
78+
Some(type_name) => type_name == descriptor.full_name(),
79+
None => false,
80+
}
81+
}
82+
83+
/// Extract a message from this `Any`.
84+
///
85+
/// # Returns
86+
///
87+
/// * `Ok(None)` when message type mismatch
88+
/// * `Err` when parse failed
89+
pub fn unpack<M: Message>(&self) -> ProtobufResult<Option<M>> {
90+
if !self.is::<M>() {
91+
return Ok(None);
92+
}
93+
Ok(Some(parse_from_bytes(&self.value)?))
94+
}
95+
96+
/// Extract a message from this `Any`.
97+
///
98+
/// # Returns
99+
///
100+
/// * `Ok(None)` when message type mismatch
101+
/// * `Err` when parse failed
102+
pub fn unpack_dyn(
103+
&self,
104+
descriptor: &MessageDescriptor,
105+
) -> ProtobufResult<Option<Box<dyn Message>>> {
106+
if !self.is_dyn(descriptor) {
107+
return Ok(None);
108+
}
109+
let mut message = descriptor.new_instance();
110+
message.merge_from_bytes(&self.value)?;
111+
message.check_initialized()?;
112+
Ok(Some(message))
113+
}
114+
}

protobuf/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub mod descriptor;
4747
pub mod plugin;
4848
pub mod rustproto;
4949

50+
mod any;
51+
5052
mod clear;
5153
mod core;
5254
mod enums;

0 commit comments

Comments
 (0)