/// wire type як він приходить по дроту.
enum WireType {
/// Varint WireType вказує на те, що значення є одним VARINT.
Varint,
/// I64 WireType вказує на те, що значення має точно 8 байт у little-endian
/// порядку та містить 64-бітне ціле зі знаком або тип з плаваючою комою подвійної точності.
//I64, -- не потрібно для цієї вправи
/// Len WireType вказує на те, що значення є довжиною, представленою у вигляді
/// VARINT за яким слідує рівно стільки байт.
Len,
// Тип WireType I32 вказує на те, що значення - це рівно 4 байти в
// little-endian порядку, що містять 32-бітне ціле число зі знаком або тип з плаваючою комою.
//I32, -- не потрібно для цієї вправи
}
#[derive(Debug)]
/// Значення поля, введене на основі wire type.
enum FieldValue<'a> {
Varint(u64),
//I64(i64), -- не потрібно для цієї вправи
Len(&'a [u8]),
//I32(i32), -- не потрібно для цієї вправи
}
#[derive(Debug)]
/// Поле, що містить номер поля та його значення.
struct Field<'a> {
field_num: u64,
value: FieldValue<'a>,
}
trait ProtoMessage<'a>: Default {
fn add_field(&mut self, field: Field<'a>);
}
impl From<u64> for WireType {
fn from(value: u64) -> Self {
match value {
0 => WireType::Varint,
//1 => WireType::I64, -- не потрібно для цієї вправи
2 => WireType::Len,
//5 => WireType::I32, -- не потрібно для цієї вправи
_ => panic!("Неправильний wire type: {value}"),
}
}
}
impl<'a> FieldValue<'a> {
fn as_str(&self) -> &'a str {
let FieldValue::Len(data) = self else {
panic!("Очікуваний рядок має бути полем `Len`");
};
std::str::from_utf8(data).expect("Неправильний рядок")
}
fn as_bytes(&self) -> &'a [u8] {
let FieldValue::Len(data) = self else {
panic!("Очікувані байти мають бути полем `Len`");
};
data
}
fn as_u64(&self) -> u64 {
let FieldValue::Varint(value) = self else {
panic!("Очікувалося, що `u64` буде полем `Varint`");
};
*value
}
}
/// Розбір VARINT з поверненням розібраного значення та решти байтів.
fn parse_varint(data: &[u8]) -> (u64, &[u8]) {
for i in 0..7 {
let Some(b) = data.get(i) else {
panic!("Недостатньо байт для varint");
};
if b & 0x80 == 0 {
// Це останній байт VARINT, тому перетворюємо його
// в u64 і повертаємо.
let mut value = 0u64;
for b in data[..=i].iter().rev() {
value = (value << 7) | (b & 0x7f) as u64;
}
return (value, &data[i + 1..]);
}
}
// Більше 7 байт є неприпустимим.
panic!("Забагато байт для varint");
}
/// Перетворити тег у номер поля та WireType.
fn unpack_tag(tag: u64) -> (u64, WireType) {
let field_num = tag >> 3;
let wire_type = WireType::from(tag & 0x7);
(field_num, wire_type)
}
/// Розбір поля з поверненням залишку байтів
fn parse_field(data: &[u8]) -> (Field, &[u8]) {
let (tag, remainder) = parse_varint(data);
let (field_num, wire_type) = unpack_tag(tag);
let (fieldvalue, remainder) = match wire_type {
WireType::Varint => {
let (value, remainder) = parse_varint(remainder);
(FieldValue::Varint(value), remainder)
}
WireType::Len => {
let (len, remainder) = parse_varint(remainder);
let len: usize = len.try_into().expect("len не є допустимим `usize`");
if remainder.len() < len {
panic!("Несподіваний EOF");
}
let (value, remainder) = remainder.split_at(len);
(FieldValue::Len(value), remainder)
}
};
(Field { field_num, value: fieldvalue }, remainder)
}
/// Розбір повідомлення за заданими даними, викликаючи `T::add_field` для кожного поля в
/// повідомленні.
///
/// Споживаються всі вхідні дані.
fn parse_message<'a, T: ProtoMessage<'a>>(mut data: &'a [u8]) -> T {
let mut result = T::default();
while !data.is_empty() {
let parsed = parse_field(data);
result.add_field(parsed.0);
data = parsed.1;
}
result
}
#[derive(PartialEq)]
#[derive(Debug, Default)]
struct PhoneNumber<'a> {
number: &'a str,
type_: &'a str,
}
#[derive(PartialEq)]
#[derive(Debug, Default)]
struct Person<'a> {
name: &'a str,
id: u64,
phone: Vec<PhoneNumber<'a>>,
}
impl<'a> ProtoMessage<'a> for Person<'a> {
fn add_field(&mut self, field: Field<'a>) {
match field.field_num {
1 => self.name = field.value.as_str(),
2 => self.id = field.value.as_u64(),
3 => self.phone.push(parse_message(field.value.as_bytes())),
_ => {} // пропустити все інше
}
}
}
impl<'a> ProtoMessage<'a> for PhoneNumber<'a> {
fn add_field(&mut self, field: Field<'a>) {
match field.field_num {
1 => self.number = field.value.as_str(),
2 => self.type_ = field.value.as_str(),
_ => {} // пропустити все інше
}
}
}
fn main() {
let person: Person = parse_message(&[
0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a,
0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35,
0x2d, 0x31, 0x32, 0x31, 0x32, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x1a,
0x18, 0x0a, 0x0e, 0x2b, 0x31, 0x38, 0x30, 0x30, 0x2d, 0x38, 0x36, 0x37,
0x2d, 0x35, 0x33, 0x30, 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c,
0x65,
]);
println!("{:#?}", person);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id() {
let person_id: Person = parse_message(&[0x10, 0x2a]);
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
}
#[test]
fn test_name() {
let person_name: Person = parse_message(&[
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
0x6e, 0x61, 0x6d, 0x65,
]);
assert_eq!(
person_name,
Person { name: "красиве ім'я", id: 0, phone: vec![] }
);
}
#[test]
fn test_just_person() {
let person_name_id: Person =
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
assert_eq!(person_name_id, Person { name: "Еван", id: 22, phone: vec![] });
}
#[test]
fn test_phone() {
let phone: Person = parse_message(&[
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
0x68, 0x6f, 0x6d, 0x65,
]);
assert_eq!(
phone,
Person {
name: "",
id: 0,
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "дім" },],
}
);
}
}