use crate::Result;
use crate::field::{Field, FieldValue};
use crate::tag::Tag;
use crate::varint::{Varint, WriteExtVarint};
use crate::wire_format::WireType;
use ::std::io::Write;
pub trait WriteExtProtobuf {
fn write_protobuf_field<L: AsRef<[u8]>>(&mut self, field: &Field<L>) -> Result<usize>;
fn write_protobuf_fields<'a, L: AsRef<[u8]> + 'a, I>(&mut self, fields: I) -> Result<usize>
where
I: IntoIterator<Item = &'a Field<L>>;
}
impl<W> WriteExtProtobuf for W
where
W: Write,
{
fn write_protobuf_field<L: AsRef<[u8]>>(&mut self, field: &Field<L>) -> Result<usize> {
let mut bytes_written = 0;
let wire_type = match &field.value {
FieldValue::Varint(_) => WireType::Varint,
FieldValue::I32(_) => WireType::Int32,
FieldValue::I64(_) => WireType::Int64,
FieldValue::Len(_) => WireType::Len,
};
let tag = Tag {
field_number: field.field_number,
wire_type,
};
let tag_varint = tag.to_encoded();
bytes_written += self.write_varint(&tag_varint)?;
match &field.value {
FieldValue::Varint(varint) => {
bytes_written += self.write_varint(varint)?;
}
FieldValue::I32(bytes) => {
self.write_all(bytes)?;
bytes_written += 4;
}
FieldValue::I64(bytes) => {
self.write_all(bytes)?;
bytes_written += 8;
}
FieldValue::Len(data) => {
let data_slice = data.as_ref();
let length_varint = Varint::from_uint64(data_slice.len() as u64);
bytes_written += self.write_varint(&length_varint)?;
self.write_all(data_slice)?;
bytes_written += data_slice.len();
}
}
Ok(bytes_written)
}
fn write_protobuf_fields<'a, L: AsRef<[u8]> + 'a, I>(&mut self, fields: I) -> Result<usize>
where
I: IntoIterator<Item = &'a Field<L>>,
{
let mut total_bytes = 0;
for field in fields {
total_bytes += self.write_protobuf_field(field)?;
}
Ok(total_bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field_number::FieldNumber;
#[test]
fn test_write_single_varint_field() {
let mut buffer = Vec::new();
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(1).unwrap(),
FieldValue::from_uint64(150),
);
let bytes_written = buffer.write_protobuf_field(&field).unwrap();
assert_eq!(bytes_written, 3); assert_eq!(buffer, vec![0x08, 0x96, 0x01]); }
#[test]
fn test_write_len_field() {
let mut buffer = Vec::new();
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::Len("Hel".to_string().into_bytes()),
);
buffer.write_protobuf_field(&field).unwrap();
assert_eq!(buffer, vec![0x12, 0x03, 0x48, 0x65, 0x6c]); }
#[test]
fn test_write_i32_field() {
let mut buffer = Vec::new();
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::from_fixed32(0x12345678),
);
buffer.write_protobuf_field(&field).unwrap();
assert_eq!(buffer, vec![0x15, 0x78, 0x56, 0x34, 0x12]); }
#[test]
fn test_write_i64_field() {
let mut buffer = Vec::new();
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(3).unwrap(),
FieldValue::from_fixed64(0x1234567890ABCDEF),
);
buffer.write_protobuf_field(&field).unwrap();
assert_eq!(
buffer,
vec![0x19, 0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12]
); }
#[test]
fn test_write_multiple_fields() {
let mut buffer = Vec::new();
let fields: Vec<Field<Vec<u8>>> = vec![
Field::new(
FieldNumber::try_from(1).unwrap(),
FieldValue::from_uint64(150),
),
Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::Len("Hel".to_string().into_bytes()),
),
];
buffer.write_protobuf_fields(&fields).unwrap();
assert_eq!(
buffer,
vec![
0x08, 0x96, 0x01, 0x12, 0x03, 0x48, 0x65, 0x6c, ]
);
}
#[test]
fn test_field_encoded_size() {
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(1).unwrap(),
FieldValue::from_uint64(150),
);
assert_eq!(field.encoded_size(), 3);
let field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::Len("Hello".to_string().into_bytes()),
);
assert_eq!(field.encoded_size(), 7); }
#[test]
fn test_fieldvalue_constructors() {
assert!(matches!(
FieldValue::<Vec<u8>>::from_uint64(42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_uint32(42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_sint64(-42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_sint32(-42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_int64(-42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_int32(-42),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_bool(true),
FieldValue::Varint(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_fixed32(42),
FieldValue::I32(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_sfixed32(-42),
FieldValue::I32(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_float(3.14),
FieldValue::I32(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_fixed64(42),
FieldValue::I64(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_sfixed64(-42),
FieldValue::I64(_)
));
assert!(matches!(
FieldValue::<Vec<u8>>::from_double(3.14),
FieldValue::I64(_)
));
assert!(matches!(FieldValue::Len(vec![1, 2, 3]), FieldValue::Len(_)));
assert!(matches!(
FieldValue::Len("test".to_string().into_bytes()),
FieldValue::Len(_)
));
}
#[cfg(feature = "read")]
mod roundtrip_tests {
use super::*;
use crate::field::read::ReadExtProtobuf;
#[test]
fn test_roundtrip_varint() {
let mut buffer = Vec::new();
let original_field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(1).unwrap(),
FieldValue::from_uint64(150),
);
buffer.write_protobuf_field(&original_field).unwrap();
let reader = buffer.as_slice();
let fields: Vec<_> = reader
.read_protobuf_fields()
.collect::<::std::result::Result<Vec<_>, _>>()
.unwrap();
assert_eq!(fields.len(), 1);
assert_eq!(fields[0], original_field);
}
#[test]
fn test_roundtrip_string() {
let mut buffer = Vec::new();
let original_field: Field<Vec<u8>> = Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::Len("Hello, Protocol Buffers!".to_string().into_bytes()),
);
buffer.write_protobuf_field(&original_field).unwrap();
let reader = buffer.as_slice();
let fields: Vec<_> = reader
.read_protobuf_fields()
.collect::<::std::result::Result<Vec<_>, _>>()
.unwrap();
assert_eq!(fields.len(), 1);
let read_field = &fields[0];
assert_eq!(*read_field, original_field);
}
#[test]
fn test_roundtrip_multiple_fields() {
let mut buffer = Vec::new();
let original_fields: Vec<Field<Vec<u8>>> = vec![
Field::new(
FieldNumber::try_from(1).unwrap(),
FieldValue::from_uint64(150),
),
Field::new(
FieldNumber::try_from(2).unwrap(),
FieldValue::Len("Hello".to_string().into_bytes()),
),
Field::new(
FieldNumber::try_from(3).unwrap(),
FieldValue::from_fixed32(0x12345678),
),
];
buffer.write_protobuf_fields(&original_fields).unwrap();
let reader = buffer.as_slice();
let read_fields: Vec<_> = reader
.read_protobuf_fields()
.collect::<::std::result::Result<Vec<_>, _>>()
.unwrap();
assert_eq!(read_fields, original_fields);
}
}
}