#![warn(missing_docs)]
#![allow(unsafe_code)]
extern crate self as mesh_protobuf;
pub mod buffer;
mod encode_with;
pub mod encoding;
pub mod inplace;
pub mod message;
pub mod oneof;
#[cfg(feature = "prost")]
pub mod prost;
pub mod protobuf;
pub mod protofile;
pub mod table;
mod time;
pub mod transparent;
pub use encode_with::EncodeAs;
pub use mesh_derive::Protobuf;
pub use time::Timestamp;
use self::table::decode::DecoderEntry;
use self::table::encode::EncoderEntry;
use inplace::InplaceOption;
use protofile::DescribeMessage;
use protofile::MessageDescription;
use protofile::TypeUrl;
use std::cell::RefCell;
use std::fmt;
use std::mem::MaybeUninit;
use std::num::Wrapping;
pub trait DefaultEncoding {
type Encoding;
}
pub trait Protobuf: DefaultEncoding<Encoding = <Self as Protobuf>::Encoding> + Sized {
type Encoding: MessageEncode<Self, NoResources>
+ for<'a> MessageDecode<'a, Self, NoResources>
+ FieldEncode<Self, NoResources>
+ for<'a> FieldDecode<'a, Self, NoResources>;
}
impl<T> Protobuf for T
where
T: DefaultEncoding,
T::Encoding: MessageEncode<T, NoResources>
+ for<'a> MessageDecode<'a, T, NoResources>
+ FieldEncode<T, NoResources>
+ for<'a> FieldDecode<'a, T, NoResources>,
{
type Encoding = <T as DefaultEncoding>::Encoding;
}
pub trait DescribedProtobuf: Protobuf {
const DESCRIPTION: MessageDescription<'static>;
const TYPE_URL: TypeUrl<'static> = Self::DESCRIPTION.type_url();
}
impl<T: DefaultEncoding + Protobuf> DescribedProtobuf for T
where
<T as DefaultEncoding>::Encoding: DescribeMessage<T>,
{
const DESCRIPTION: MessageDescription<'static> =
<<T as DefaultEncoding>::Encoding as DescribeMessage<T>>::DESCRIPTION;
}
pub trait MessageEncode<T, R>: Sized {
fn write_message(item: T, writer: protobuf::MessageWriter<'_, '_, R>);
fn compute_message_size(item: &mut T, sizer: protobuf::MessageSizer<'_>);
}
pub trait MessageDecode<'a, T, R>: Sized {
fn read_message(
item: &mut InplaceOption<'_, T>,
reader: protobuf::MessageReader<'a, '_, R>,
) -> Result<()>;
}
pub trait FieldEncode<T, R>: Sized {
fn write_field(item: T, writer: protobuf::FieldWriter<'_, '_, R>);
fn compute_field_size(item: &mut T, sizer: protobuf::FieldSizer<'_>);
fn packed<'a>() -> Option<&'a dyn PackedEncode<T>>
where
T: 'a,
{
None
}
fn wrap_in_sequence() -> bool {
false
}
fn write_field_in_sequence(item: T, writer: &mut protobuf::SequenceWriter<'_, '_, R>) {
if Self::wrap_in_sequence() {
WrappedField::<Self>::write_field(item, writer.field())
} else {
Self::write_field(item, writer.field())
}
}
fn compute_field_size_in_sequence(item: &mut T, sizer: &mut protobuf::SequenceSizer<'_>) {
if Self::wrap_in_sequence() {
WrappedField::<Self>::compute_field_size(item, sizer.field())
} else {
Self::compute_field_size(item, sizer.field())
}
}
const ENTRY: EncoderEntry<T, R> = EncoderEntry::custom::<Self>();
}
pub trait PackedEncode<T> {
fn write_packed(&self, data: &[T], writer: protobuf::PackedWriter<'_, '_>);
fn compute_packed_size(&self, data: &[T], sizer: protobuf::PackedSizer<'_>);
fn must_pack(&self) -> bool;
}
pub trait FieldDecode<'a, T, R>: Sized {
fn read_field(
item: &mut InplaceOption<'_, T>,
reader: protobuf::FieldReader<'a, '_, R>,
) -> Result<()>;
fn default_field(item: &mut InplaceOption<'_, T>) -> Result<()>;
fn packed<'p, C: CopyExtend<T>>() -> Option<&'p dyn PackedDecode<'a, T, C>>
where
T: 'p,
{
None
}
fn wrap_in_sequence() -> bool {
false
}
fn read_field_in_sequence(
item: &mut InplaceOption<'_, T>,
reader: protobuf::FieldReader<'a, '_, R>,
) -> Result<()> {
if Self::wrap_in_sequence() {
WrappedField::<Self>::read_field(item, reader)
} else {
Self::read_field(item, reader)
}
}
const ENTRY: DecoderEntry<'a, T, R> = DecoderEntry::custom::<Self>();
}
pub trait PackedDecode<'a, T, C> {
fn read_packed(&self, data: &mut C, reader: &mut protobuf::PackedReader<'a>) -> Result<()>;
fn must_pack(&self) -> bool;
}
pub trait CopyExtend<T> {
fn push(&mut self, item: T)
where
T: Copy;
fn extend_from_slice(&mut self, items: &[T])
where
T: Copy;
}
impl<T> CopyExtend<T> for Vec<T> {
fn push(&mut self, item: T)
where
T: Copy,
{
self.push(item);
}
fn extend_from_slice(&mut self, items: &[T])
where
T: Copy,
{
self.extend_from_slice(items);
}
}
struct WrappedField<E>(pub E);
impl<T, R, E: FieldEncode<T, R>> FieldEncode<T, R> for WrappedField<E> {
fn write_field(item: T, writer: protobuf::FieldWriter<'_, '_, R>) {
writer.message(|mut writer| E::write_field(item, writer.field(1)));
}
fn compute_field_size(item: &mut T, sizer: protobuf::FieldSizer<'_>) {
sizer.message(|mut sizer| E::compute_field_size(item, sizer.field(1)));
}
}
impl<'a, T, R, E: FieldDecode<'a, T, R>> FieldDecode<'a, T, R> for WrappedField<E> {
fn read_field(
item: &mut InplaceOption<'_, T>,
reader: protobuf::FieldReader<'a, '_, R>,
) -> Result<()> {
for field in reader.message()? {
let (number, reader) = field?;
if number == 1 {
E::read_field(item, reader)?;
}
}
if item.is_none() {
E::default_field(item)?;
}
Ok(())
}
fn default_field(item: &mut InplaceOption<'_, T>) -> Result<()> {
E::default_field(item)
}
}
pub fn encode<T: DefaultEncoding>(message: T) -> Vec<u8>
where
T::Encoding: MessageEncode<T, NoResources>,
{
protobuf::Encoder::new(message).encode().0
}
pub fn decode<'a, T: DefaultEncoding>(data: &'a [u8]) -> Result<T>
where
T::Encoding: MessageDecode<'a, T, NoResources>,
{
inplace_none!(message: T);
protobuf::decode_with::<T::Encoding, _, _>(&mut message, data, &mut [])?;
Ok(message.take().expect("should be constructed"))
}
pub fn merge<'a, T: DefaultEncoding>(value: T, data: &'a [u8]) -> Result<T>
where
T::Encoding: MessageDecode<'a, T, NoResources>,
{
inplace_some!(value);
protobuf::decode_with::<T::Encoding, _, _>(&mut value, data, &mut [])?;
Ok(value.take().expect("should be constructed"))
}
pub trait Downcast<T> {}
pub trait Upcast<T> {}
impl<T, U: Downcast<T>> Upcast<U> for T {}
pub enum NoResources {}
#[derive(Debug)]
pub struct SerializedMessage<R = NoResources> {
pub data: Vec<u8>,
pub resources: Vec<R>,
}
impl<R> Default for SerializedMessage<R> {
fn default() -> Self {
Self {
data: Default::default(),
resources: Default::default(),
}
}
}
impl<R> SerializedMessage<R> {
pub fn from_message<T: DefaultEncoding>(t: T) -> Self
where
T::Encoding: MessageEncode<T, R>,
{
let (data, resources) = protobuf::Encoder::new(t).encode();
Self { data, resources }
}
pub fn into_message<T: DefaultEncoding>(self) -> Result<T>
where
T::Encoding: for<'a> MessageDecode<'a, T, R>,
{
let (data, mut resources) = self.prep_decode();
inplace_none!(message: T);
protobuf::decode_with::<T::Encoding, _, _>(&mut message, &data, &mut resources)?;
Ok(message.take().expect("should be constructed"))
}
fn prep_decode(self) -> (Vec<u8>, Vec<Option<R>>) {
let data = self.data;
let resources = self.resources.into_iter().map(Some).collect();
(data, resources)
}
}
#[derive(Debug)]
pub struct Error(Box<ErrorInner>);
#[derive(Debug)]
struct ErrorInner {
types: Vec<&'static str>,
err: Box<dyn std::error::Error + Send + Sync>,
}
#[derive(Debug, thiserror::Error)]
enum DecodeError {
#[error("expected a message")]
ExpectedMessage,
#[error("expected a resource")]
ExpectedResource,
#[error("expected a varint")]
ExpectedVarInt,
#[error("expected a fixed64")]
ExpectedFixed64,
#[error("expected a fixed32")]
ExpectedFixed32,
#[error("expected a byte array")]
ExpectedByteArray,
#[error("field cannot exist")]
Unexpected,
#[error("eof parsing a varint")]
EofVarInt,
#[error("eof parsing a fixed64")]
EofFixed64,
#[error("eof parsing a fixed32")]
EofFixed32,
#[error("eof parsing a byte array")]
EofByteArray,
#[error("varint too big")]
VarIntTooBig,
#[error("missing resource")]
MissingResource,
#[error("invalid resource range")]
InvalidResourceRange,
#[error("unknown wire type {0}")]
UnknownWireType(u32),
#[error("invalid UTF-32 character")]
InvalidUtf32,
#[error("wrong buffer size for u128")]
BadU128,
#[error("invalid UTF-8 string")]
InvalidUtf8(#[source] std::str::Utf8Error),
#[error("missing required field")]
MissingRequiredField,
#[error("wrong packed array length")]
BadPackedArrayLength,
#[error("wrong array length")]
BadArrayLength,
#[error("duration out of range")]
DurationRange,
}
impl Error {
pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> Self {
Self(Box::new(ErrorInner {
types: Vec::new(),
err: error.into(),
}))
}
pub fn typed<T>(mut self) -> Self {
self.0.types.push(std::any::type_name::<T>());
self
}
}
impl From<DecodeError> for Error {
fn from(kind: DecodeError) -> Self {
Self(Box::new(ErrorInner {
types: Vec::new(),
err: kind.into(),
}))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(&ty) = self.0.types.last() {
write!(f, "decoding failed in {}", ty)?;
for &ty in self.0.types.iter().rev().skip(1) {
write!(f, "/{}", ty)?;
}
Ok(())
} else {
write!(f, "decoding failed")
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(self.0.err.as_ref())
}
}
pub trait ResultExt {
fn typed<T>(self) -> Self;
}
impl<T> ResultExt for Result<T> {
fn typed<U>(self) -> Self {
self.map_err(Error::typed::<U>)
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::encode;
use super::SerializedMessage;
use crate::decode;
use crate::encoding::BorrowedCowField;
use crate::encoding::OwningCowField;
use crate::encoding::VecField;
use crate::DecodeError;
use crate::FieldDecode;
use crate::FieldEncode;
use crate::NoResources;
use mesh_derive::Protobuf;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::convert::Infallible;
use std::error::Error;
use std::num::NonZeroU32;
use std::time::Duration;
#[track_caller]
fn assert_roundtrips<T>(t: T)
where
T: crate::DefaultEncoding + Clone + Eq + std::fmt::Debug,
T::Encoding:
crate::MessageEncode<T, NoResources> + for<'a> crate::MessageDecode<'a, T, NoResources>,
{
println!("{t:?}");
let v = encode(t.clone());
println!("{v:x?}");
let t2 = decode::<T>(&v).unwrap();
assert_eq!(t, t2);
}
#[track_caller]
fn assert_field_roundtrips<T>(t: T)
where
T: crate::DefaultEncoding + Clone + Eq + std::fmt::Debug,
T::Encoding: FieldEncode<T, NoResources> + for<'a> FieldDecode<'a, T, NoResources>,
{
assert_roundtrips((t,));
}
#[test]
fn test_field() {
assert_field_roundtrips(5u32);
assert_field_roundtrips(true);
assert_field_roundtrips("hi".to_string());
assert_field_roundtrips(5u128);
assert_field_roundtrips(());
assert_field_roundtrips((1, 2));
assert_field_roundtrips(("foo".to_string(), "bar".to_string()));
assert_field_roundtrips([1, 2, 3]);
assert_field_roundtrips(["abc".to_string(), "def".to_string()]);
assert_field_roundtrips(Some(5));
assert_field_roundtrips(Option::<u32>::None);
assert_field_roundtrips(vec![1, 2, 3]);
assert_field_roundtrips(vec!["abc".to_string(), "def".to_string()]);
assert_field_roundtrips(Some(Some(true)));
assert_field_roundtrips(Some(Option::<bool>::None));
assert_field_roundtrips(vec![None, Some(true), None]);
assert_field_roundtrips(HashMap::from_iter([(5u32, 6u32), (4, 2)]));
assert_field_roundtrips(BTreeMap::from_iter([
("hi".to_owned(), 6u32),
("hmm".to_owned(), 2),
]));
}
#[test]
fn test_nonzero() {
assert_field_roundtrips(NonZeroU32::new(1).unwrap());
assert_eq!(encode((5u32,)), encode((NonZeroU32::new(5).unwrap(),)));
assert_eq!(
decode::<(NonZeroU32,)>(&encode((Some(0u32),)))
.unwrap_err()
.source()
.unwrap()
.to_string(),
"value must be non-zero"
)
}
#[test]
fn test_derive_struct() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
x: u32,
y: u32,
z: String,
w: Option<bool>,
}
let foo = Foo {
x: 5,
y: 104824,
z: "alphabet".to_owned(),
w: None,
};
assert_roundtrips(foo);
}
#[test]
fn test_nested_derive_struct() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
x: u32,
y: u32,
b: Option<Bar>,
}
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Bar {
a: Option<bool>,
b: u32,
}
let foo = Foo {
x: 5,
y: 104824,
b: Some(Bar {
a: Some(true),
b: 5,
}),
};
assert_roundtrips(foo);
}
#[test]
fn test_derive_enum() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
enum Foo {
A,
B(u32, String),
C { x: bool, y: u32 },
}
assert_roundtrips(Foo::A);
assert_roundtrips(Foo::B(12, "hi".to_owned()));
assert_roundtrips(Foo::C { x: true, y: 0 });
assert_roundtrips(Foo::C { x: false, y: 0 });
}
#[test]
fn test_vec() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
u32: Vec<u32>,
u8: Vec<u8>,
vec_no_pack: Vec<(u32,)>,
vec_of_vec8: Vec<Vec<u8>>,
vec_of_vec32: Vec<Vec<u32>>,
vec_of_vec_no_pack: Vec<Vec<(u32,)>>,
}
let foo = Foo {
u32: vec![1, 2, 3, 4, 5],
u8: b"abcdefg".to_vec(),
vec_no_pack: vec![(1,), (2,), (3,), (4,), (5,)],
vec_of_vec8: vec![b"abc".to_vec(), b"def".to_vec()],
vec_of_vec32: vec![vec![1, 2, 3], vec![4, 5, 6]],
vec_of_vec_no_pack: vec![vec![(64,), (65,)], vec![(66,), (67,)]],
};
assert_roundtrips(foo);
}
struct NoPackU32;
impl<R> FieldEncode<u32, R> for NoPackU32 {
fn write_field(item: u32, writer: crate::protobuf::FieldWriter<'_, '_, R>) {
writer.varint(item.into())
}
fn compute_field_size(item: &mut u32, sizer: crate::protobuf::FieldSizer<'_>) {
sizer.varint((*item).into())
}
}
impl<R> FieldDecode<'_, u32, R> for NoPackU32 {
fn read_field(
_item: &mut crate::inplace::InplaceOption<'_, u32>,
_reader: crate::protobuf::FieldReader<'_, '_, R>,
) -> crate::Result<()> {
unimplemented!()
}
fn default_field(_item: &mut crate::inplace::InplaceOption<'_, u32>) -> crate::Result<()> {
unimplemented!()
}
}
#[test]
fn test_vec_alt() {
{
#[derive(Protobuf, Clone)]
struct NoPack {
#[mesh(encoding = "VecField<NoPackU32>")]
v: Vec<u32>,
}
#[derive(Protobuf)]
struct CanPack {
v: Vec<u32>,
}
let no_pack = NoPack { v: vec![1, 2, 3] };
let v = encode(no_pack.clone());
println!("{v:x?}");
let can_pack = decode::<CanPack>(&v).unwrap();
assert_eq!(no_pack.v, can_pack.v);
}
{
#[derive(Protobuf, Clone)]
struct NoPackNest {
#[mesh(encoding = "VecField<VecField<NoPackU32>>")]
v: Vec<Vec<u32>>,
}
#[derive(Protobuf)]
struct CanPackNest {
v: Vec<Vec<u32>>,
}
let no_pack = NoPackNest {
v: vec![vec![1, 2, 3], vec![4, 5, 6]],
};
let v = encode(no_pack.clone());
println!("{v:x?}");
let can_pack = decode::<CanPackNest>(&v).unwrap();
assert_eq!(no_pack.v, can_pack.v);
}
}
#[test]
fn test_merge() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Bar(u32);
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
enum Enum {
A(u32),
B(Option<u32>, Vec<u8>),
}
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
x: u32,
y: u32,
z: String,
w: Option<bool>,
v: Vec<u32>,
v8: Vec<u8>,
vb: Vec<Bar>,
e: Enum,
}
let foo = Foo {
x: 1,
y: 2,
z: "abc".to_string(),
w: Some(true),
v: vec![1, 2, 3],
v8: b"xyz".to_vec(),
vb: vec![Bar(1), Bar(2)],
e: Enum::B(Some(1), b"abc".to_vec()),
};
assert_roundtrips(foo.clone());
let foo2 = Foo {
x: 3,
y: 4,
z: "def".to_string(),
w: None,
v: vec![4, 5, 6],
v8: b"uvw".to_vec(),
vb: vec![Bar(3), Bar(4), Bar(5)],
e: Enum::B(None, b"def".to_vec()),
};
assert_roundtrips(foo2.clone());
let foo3 = Foo {
x: 3,
y: 4,
z: "def".to_string(),
w: Some(true),
v: vec![1, 2, 3, 4, 5, 6],
v8: b"xyzuvw".to_vec(),
vb: vec![Bar(1), Bar(2), Bar(3), Bar(4), Bar(5)],
e: Enum::B(Some(1), b"abcdef".to_vec()),
};
assert_roundtrips(foo3.clone());
let foo = super::merge(foo, &<SerializedMessage>::from_message(foo2).data).unwrap();
assert_eq!(foo, foo3);
}
#[test]
fn test_alternate_encoding() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
sint32: i32,
#[mesh(encoding = "mesh_protobuf::encoding::VarintField")]
int32: i32,
}
assert_roundtrips(Foo {
int32: -1,
sint32: -1,
});
assert_eq!(
&encode(Foo {
sint32: -1,
int32: -1,
}),
&[8, 1, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]
);
}
#[test]
fn test_array() {
assert_field_roundtrips([1, 2, 3]);
assert_field_roundtrips(["a".to_string(), "b".to_string(), "c".to_string()]);
assert_field_roundtrips([vec![1, 2, 3], vec![4, 5, 6]]);
assert_field_roundtrips([vec![1u8, 2]]);
assert_field_roundtrips([[0_u8, 1], [2, 3]]);
assert_field_roundtrips([Vec::<()>::new()]);
assert_field_roundtrips([vec!["abc".to_string()]]);
}
#[test]
fn test_nested() {
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Nested<T> {
pub n: u32,
pub foo: T,
}
#[derive(Protobuf, Debug, Clone, PartialEq, Eq)]
struct Foo {
x: u32,
y: u32,
z: String,
w: Option<bool>,
}
let t = Nested {
n: 5,
foo: Foo {
x: 5,
y: 104824,
z: "alphabet".to_owned(),
w: None,
},
};
let t2: Nested<SerializedMessage> = SerializedMessage::from_message(t.clone())
.into_message()
.unwrap();
let t3: Nested<Foo> = SerializedMessage::from_message(t2).into_message().unwrap();
assert_eq!(t, t3);
}
#[test]
fn test_lifetime() {
#[derive(Protobuf)]
struct Foo<'a>(&'a str);
let s = String::from("foo");
let v = encode(Foo(&s));
let foo: Foo<'_> = decode(&v).unwrap();
assert_eq!(foo.0, &s);
}
#[test]
fn test_generic_lifetime() {
#[derive(Protobuf)]
struct Foo<T>(T);
let s = String::from("foo");
let v = encode(Foo(s.as_str()));
let foo: Foo<&str> = decode(&v).unwrap();
assert_eq!(foo.0, &s);
}
#[test]
fn test_infallible() {
assert!(matches!(
decode::<Infallible>(&[])
.unwrap_err()
.source()
.unwrap()
.downcast_ref::<DecodeError>(),
Some(DecodeError::Unexpected)
));
}
#[test]
fn test_empty_message() {
#[derive(Protobuf)]
struct Message(u32);
let v = encode(((Message(0),),));
assert_eq!(&v, b"");
let _message: ((Message,),) = decode(&[]).unwrap();
}
#[test]
fn test_nested_empty_message() {
#[derive(Debug, Clone, PartialEq, Eq, Protobuf)]
struct Message(Outer, Inner);
#[derive(Debug, Default, Clone, PartialEq, Eq, Protobuf)]
struct Outer(Inner);
#[derive(Debug, Default, Clone, PartialEq, Eq, Protobuf)]
struct Inner(u32);
assert_roundtrips(Message(Default::default(), Inner(1)));
}
#[test]
fn test_transparent_message() {
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
struct Inner(u32);
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
#[mesh(transparent)]
struct TupleStruct(Inner);
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
#[mesh(transparent)]
struct NamedStruct {
x: Inner,
}
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
#[mesh(transparent)]
struct GenericStruct<T>(T);
assert_roundtrips(TupleStruct(Inner(5)));
assert_eq!(encode(TupleStruct(Inner(5))), encode(Inner(5)));
assert_eq!(encode(NamedStruct { x: Inner(5) }), encode(Inner(5)));
assert_eq!(encode(GenericStruct(Inner(5))), encode(Inner(5)));
}
#[test]
fn test_transparent_field() {
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
#[mesh(transparent)]
struct Inner(u32);
#[derive(Protobuf, Copy, Clone, PartialEq, Eq, Debug)]
struct Outer<T>(T);
assert_roundtrips(Outer(Inner(5)));
assert_eq!(encode(Outer(Inner(5))), encode(Outer(5u32)));
}
#[test]
fn test_transparent_enum() {
#[derive(Protobuf, Clone, PartialEq, Eq, Debug)]
enum Foo {
#[mesh(transparent)]
Bar(u32),
#[mesh(transparent)]
Option(Option<u32>),
#[mesh(transparent)]
Vec(Vec<u32>),
#[mesh(transparent)]
VecNoPack(Vec<(u32,)>),
}
assert_roundtrips(Foo::Bar(0));
assert_eq!(encode(Foo::Bar(0)), encode((Some(0),)));
assert_roundtrips(Foo::Option(Some(5)));
assert_roundtrips(Foo::Option(None));
assert_roundtrips(Foo::Vec(vec![]));
assert_roundtrips(Foo::Vec(vec![5]));
assert_roundtrips(Foo::VecNoPack(vec![(5,)]));
}
#[test]
fn test_cow() {
#[derive(Protobuf)]
struct OwnedString<'a>(#[mesh(encoding = "OwningCowField")] Cow<'a, str>);
#[derive(Protobuf)]
struct BorrowedString<'a>(#[mesh(encoding = "BorrowedCowField")] Cow<'a, str>);
#[derive(Protobuf)]
struct OwnedBytes<'a>(#[mesh(encoding = "OwningCowField")] Cow<'a, [u8]>);
#[derive(Protobuf)]
struct BorrowedBytes<'a>(#[mesh(encoding = "BorrowedCowField")] Cow<'a, [u8]>);
let s_owning: OwnedString<'_>;
let v_owning: OwnedBytes<'_>;
{
let b = encode(("abc",));
let mut b2 = b.clone();
b2.extend(encode(("def",)));
let s_borrowed: BorrowedString<'_>;
let v_borrowed: BorrowedBytes<'_>;
let v_borrowed2: BorrowedBytes<'_>;
{
let (s,): (String,) = decode(&b2).unwrap();
assert_eq!(&s, "def");
let (v,): (Vec<u8>,) = decode(&b2).unwrap();
assert_eq!(&v, b"abcdef");
s_owning = decode(&b2).unwrap();
let s_owning = s_owning.0;
assert!(matches!(s_owning, Cow::Owned(_)));
assert_eq!(s_owning.as_ref(), "def");
s_borrowed = decode(&b2).unwrap();
let s_borrowed = s_borrowed.0;
assert!(matches!(s_borrowed, Cow::Borrowed(_)));
assert_eq!(s_borrowed.as_ref(), "def");
v_owning = decode(&b2).unwrap();
let v_owning = v_owning.0;
assert!(matches!(v_owning, Cow::Owned(_)));
assert_eq!(v_owning.as_ref(), b"abcdef");
v_borrowed = decode(&b).unwrap();
let v_borrowed = v_borrowed.0;
assert!(matches!(v_borrowed, Cow::Borrowed(_)));
assert_eq!(v_borrowed.as_ref(), b"abc");
v_borrowed2 = decode(&b2).unwrap();
let v_borrowed2 = v_borrowed2.0;
assert!(matches!(v_borrowed2, Cow::Owned(_)));
assert_eq!(v_borrowed2.as_ref(), b"abcdef");
}
}
}
#[test]
fn test_duration() {
assert_roundtrips(Duration::ZERO);
assert_roundtrips(Duration::from_secs(1));
assert_roundtrips(Duration::from_secs(1) + Duration::from_nanos(10000));
assert_roundtrips(Duration::from_secs(1) - Duration::from_nanos(10000));
decode::<Duration>(&encode((-1i64 as u64, 0u32))).unwrap_err();
assert_eq!(
decode::<Duration>(&encode((1u64, 1u32))).unwrap(),
Duration::from_secs(1) + Duration::from_nanos(1)
);
}
#[test]
fn test_failure_recovery() {
let m = encode(("foo", 2, 3));
decode::<(String, String, String)>(&m).unwrap_err();
}
}