use crate::decode;
use crate::encode;
use crate::encoding::MessageEncoding;
use crate::inplace::InplaceOption;
use crate::protobuf::MessageReader;
use crate::protobuf::MessageSizer;
use crate::protobuf::MessageWriter;
use crate::protofile::DescribeField;
use crate::protofile::FieldType;
use crate::protofile::MessageDescription;
use crate::table::DescribeTable;
use crate::DefaultEncoding;
use crate::DescribedProtobuf;
use crate::Error;
use crate::MessageDecode;
use crate::MessageEncode;
use crate::Protobuf;
use thiserror::Error;
#[derive(Debug)]
pub struct ProtobufMessage(Vec<u8>);
impl ProtobufMessage {
pub fn new(data: impl Protobuf) -> Self {
Self(encode(data))
}
pub fn parse<T: Protobuf>(&self) -> Result<T, Error> {
decode(&self.0)
}
}
impl DefaultEncoding for ProtobufMessage {
type Encoding = MessageEncoding<ProtobufMessageEncoding>;
}
impl DescribeField<ProtobufMessage> for MessageEncoding<ProtobufMessageEncoding> {
const FIELD_TYPE: FieldType<'static> = FieldType::builtin("bytes");
}
#[derive(Debug)]
pub struct ProtobufMessageEncoding;
impl<R> MessageEncode<ProtobufMessage, R> for ProtobufMessageEncoding {
fn write_message(item: ProtobufMessage, mut writer: MessageWriter<'_, '_, R>) {
writer.bytes(&item.0);
}
fn compute_message_size(item: &mut ProtobufMessage, mut sizer: MessageSizer<'_>) {
sizer.bytes(item.0.len());
}
}
impl<R> MessageDecode<'_, ProtobufMessage, R> for ProtobufMessageEncoding {
fn read_message(
item: &mut InplaceOption<'_, ProtobufMessage>,
reader: MessageReader<'_, '_, R>,
) -> crate::Result<()> {
item.get_or_insert_with(|| ProtobufMessage(Vec::new()))
.0
.extend(reader.bytes());
Ok(())
}
}
#[derive(Debug, Protobuf)]
pub struct ProtobufAny {
#[mesh(1)]
type_url: String, #[mesh(2)]
value: ProtobufMessage,
}
#[derive(Debug, Error)]
#[error("protobuf type mismatch, expected {expected}, got {actual}")]
struct TypeMismatch {
expected: String,
actual: String,
}
impl DescribeTable for ProtobufAny {
const DESCRIPTION: MessageDescription<'static> = MessageDescription::External {
name: "google.protobuf.Any",
import_path: "google/protobuf/any.proto",
};
}
impl ProtobufAny {
pub fn new<T: DescribedProtobuf>(data: T) -> Self {
Self {
type_url: T::TYPE_URL.to_string(),
value: ProtobufMessage::new(data),
}
}
pub fn parse<T: DescribedProtobuf>(&self) -> Result<T, Error> {
if &T::TYPE_URL != self.type_url.as_str() {
return Err(Error::new(TypeMismatch {
expected: T::TYPE_URL.to_string(),
actual: self.type_url.clone(),
}));
}
self.value.parse()
}
pub fn is_message<T: DescribedProtobuf>(&self) -> bool {
&T::TYPE_URL == self.type_url.as_str()
}
}
#[cfg(test)]
mod tests {
use crate::encode;
use crate::message::ProtobufAny;
use crate::message::ProtobufMessage;
use crate::Protobuf;
#[test]
fn test_message() {
let message = (5u32,);
assert_eq!(
ProtobufMessage::new(message).parse::<(u32,)>().unwrap(),
message
);
assert_eq!(encode(ProtobufMessage::new(message)), encode(message));
}
#[test]
fn test_any() {
#[derive(Protobuf, PartialEq, Eq, Copy, Clone, Debug)]
#[mesh(package = "test")]
struct Message {
#[mesh(1)]
x: u32,
}
#[derive(Protobuf, Debug)]
#[mesh(package = "test")]
struct Other {
#[mesh(1)]
x: u32,
}
let msg = Message { x: 5 };
let any = ProtobufAny::new(msg);
assert_eq!(any.type_url, "type.googleapis.com/test.Message");
assert!(any.is_message::<Message>());
assert!(!any.is_message::<Other>());
assert_eq!(any.parse::<Message>().unwrap(), msg);
println!("{:?}", any.parse::<Other>().unwrap_err());
}
}