Решение на Network Packets от Стела Маринова

Обратно към всички решения

Към профила на Стела Маринова

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 11 успешни тест(а)
  • 4 неуспешни тест(а)

Код

use std::fmt;
use std::cmp::min;
use std::convert::TryInto;
/// Грешките, които ще очакваме да върнете. По-долу ще е описано кои от тези грешки очакваме да се
/// върнат в каква ситуация.
///
#[derive(Debug)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
/// Нужна е имплементация на Display за грешките, за да може да имплементират `std::error::Error`.
/// Свободни сте да напишете каквито искате съобщения, ще тестваме само типовете, не низовия им
/// вид.
///
/// Ако са във формат на хайку, няма да получите бонус точки, но може да получите чувство на
/// вътрешно удовлетворение.
///
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: Error has become a thing!", self)
}
}
/// Тази имплементация би трябвало да сработи директно благодарение на горните. При желание, можете
/// да си имплементирате ръчно някои от методите, само внимавайте.
///
impl std::error::Error for PacketError {}
///Помощна функция за пресмятане на checksum
///
pub fn sum_of_bytes(message: &[u8])->u32{
let mut sum: u32 = 0;
for x in message{
sum += (*x) as u32;
}
return sum;
}
///Помощна функция за разделяне на информация на две
///Основната й работа е да реши къде да я раздели - изнасяне на проверката за грешки тук,
///вместо навсякъде, където се наложи
///
pub fn split(source: &[u8], pos: usize) -> (&[u8], &[u8]){
if pos >= source.len() {
let empt: &[u8] = &[];
return (source, empt);
}
return source.split_at(pos);
}
/// Един пакет, съдържащ част от съобщението. Изберете сами какви полета да използвате за
/// съхранение.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
#[derive(PartialEq, Debug)]
pub struct Packet {
version: u8,
size: u8,
message: Vec<u8>,
checksum: u32,
}
impl Packet {
/// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
/// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
/// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
/// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
///
/// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
/// литерал като b"abcd".
///
/// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
/// дължината на `source` (и остатъка ще е празен slice).
///
/// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
/// просто е невалидно, програмистка грешка).
///
pub fn from_source(source: &[u8], size: u8) -> (Self, &[u8]) {
if size == 0{
panic!("Size of package is 0!")
}
let (message, rest) = split(source, size as usize);
let packet = Packet{
version: 1,
size: min(size, source.len() as u8),
message: Vec::from(message),
checksum: sum_of_bytes(message),
};
return (packet, rest);
}
/// Връща само slice-а който пакета опакова. Тоест, ако сме конструирали пакета със
/// `Packet::from_source(b"abc", 3)`, очакваме `.payload()` да ни върне `b"abc"`.
///
/// Защо това просто не е публично property? За да не позволяваме мутация, а само конструиране
/// и четене.
///
pub fn payload(&self) -> &[u8] {
let msg: &[u8] = &self.message;
return msg;
}
/// Сериализира пакета, тоест превръща го в байтове, готови за трансфер. Версия, дължина,
/// съобщение (payload), checksum. Вижте по-горе за детайлно обяснение.
///
pub fn serialize(&self) -> Vec<u8> {
let mut data:Vec<u8> = self.message.clone();
data.insert(0, self.version);
data.insert(1, self.size);
data.append(&mut self.checksum.to_be_bytes().to_vec());
return data;
}
/// Имайки slice от байтове, искаме да извадим един пакет от началото и да върнем остатъка,
/// пакетиран в `Result`.
///
/// Байтовете са репрезентация на пакет -- версия, размер, и т.н. както е описано по-горе.
///
/// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси
/// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
///
/// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`.
///
/// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
/// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
///
/// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
/// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
/// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
/// `PacketError::InvalidChecksum`. Малко тъпо! Но уви, протоколите имат подобни тъпи ръбове,
/// особено като са написани за един уикенд. Авторите обещават по-добър протокол за версия 2.
///
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
if bytes.len() < 8{
return Err(PacketError::InvalidPacket)
}
let version:u8 = bytes[0];
if version != 1 {
return Err(PacketError::UnknownProtocolVersion);
}
let size:u8 = bytes[1];
let n:usize = 2 + size as usize;
if bytes.len() < n || size < 1 {
return Err(PacketError::InvalidPacket);
}
let message:Vec<u8> = bytes[2..n].to_vec();
if bytes.len() < 4+n {
return Err(PacketError::InvalidPacket);
}
let checkbytes:[u8;4] = bytes[n..n+4].try_into().unwrap();
let checksum:u32 = u32::from_be_bytes(checkbytes);
if checksum != sum_of_bytes(&message) {
return Err(PacketError::InvalidChecksum);
}
let p:Packet = Packet{
version,
size,
message,
checksum,
};
return Ok((p, &bytes[n+4..]));
}
}
/// Структура, която ще служи за итериране по пакети. Ще я конструираме от някакво съобщение, и
/// итерацията ще връща всеки следващ пакет, докато съобщението не бъде напълно "изпратено".
/// Изберете каквито полета ви трябват.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
pub struct PacketSerializer {
packets:Vec<Packet>,
}
impl Iterator for PacketSerializer {
type Item = Packet;
fn next(&mut self) -> Option<Self::Item> {
if !self.packets.is_empty(){
let item:Packet = self.packets.remove(0);
return Some(item);
}
return None;
}
}
/// Този trait ще ни позволи да конвертираме един `String` (а ако искаме, и други неща) от и до
/// комплект от байтове за прехвърляне по мрежата.
///
/// Детайли за методите вижте по-долу в имплементацията на този trait за `String`.
///
pub trait Packetable: Sized {
fn to_packets(&self, packet_size: u8) -> PacketSerializer;
fn to_packet_data(&self, packet_size: u8) -> Vec<u8>;
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError>;
}
impl Packetable for String {
/// Този метод приема размер, който да използваме за размера на payload-а на всеки пакет. Връща
/// итератор върху въпросните пакети. Низа трябва да се използва под формата на байтове.
///
/// Както при `.from_source`, ако подадения `packet_size` е по-голям от дължината на оставащите
/// байтове, приемаме, че размера на съответния пакет ще е колкото остава.
///
fn to_packets(&self, packet_size: u8) -> PacketSerializer {
let size = packet_size as usize;
let mut packets:Vec<Packet> = Vec::new();
let mut rest = self.as_bytes();
while !rest.is_empty(){
let (a, b) = split(rest, size);
let data = std::str::from_utf8(a).unwrap();
rest = b;
let (packet, _) = Packet::deserialize(&(String::from(data).to_packet_data(packet_size))).ok().unwrap();

Няма нужда индивидуалния пакет да има payload, който е валиден низ. Ако изберем произволен packet size, може да се окаже, че сме отрязали низа по средата на някоя буква. Което е ок -- пакетите имат за цел да бъдат транспортен механизъм.

packets.push(packet);
}
return PacketSerializer{packets: packets};
}
/// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
/// от байтове със `.serialize()` и да го натъпчем във вектора.
///
/// Както при `.from_source`, ако подадения `packet_size` е по-голям от дължината на оставащите
/// байтове, приемаме, че размера на съответния пакет ще е колкото остава.
///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let (packet, _) = Packet::from_source(self.as_bytes(), packet_size);
return packet.serialize();
}

Логиката тук не е съвсем правилна. Идеята беше не да сериализираш един пакет, а да разделиш низа на байтове с дадения размер, да пакетираш парчетата в пакети (което можеше да направиш с поредни извиквания на Packet::from_source) и да събереш сериализациите на тези пакети.

Ти си обърнала логиката -- този метод се вика като помощна функция на горния, а имаше логика да е обратното. Не сме се разбрали за условието.

/// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
/// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
/// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
///
/// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
///
/// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
/// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
/// Тогава връщаме `PacketError::CorruptedMessage`.
///
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
if packet_data.len() < 8 {
return Err(PacketError::InvalidPacket)
}
if packet_data[0] != 1 {
return Err(PacketError::UnknownProtocolVersion);
}
let size:usize = packet_data[1] as usize;
if size < 1 || packet_data.len() < 6+size {
return Err(PacketError::InvalidPacket);
}
let data:&str = std::str::from_utf8(&packet_data[2..2+size]).unwrap();
let string:Result<[u8;4], _> = packet_data[2+size..6+size].try_into();
if string.is_err(){
return Err(PacketError::CorruptedMessage);
}
let checkbytes:[u8;4] = string.unwrap();
let checksum:u32 = u32::from_be_bytes(checkbytes);
if checksum != sum_of_bytes(data.as_bytes()){
return Err(PacketError::InvalidChecksum);
}
return Ok(data.to_string());
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20200111-2173579-d2fc30/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.71s
     Running target/debug/deps/solution-a73e64ec87929bd0

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-38971695424b36d5

running 15 tests
test solution_test::test_construct_packet_from_unicode ... ok
test solution_test::test_construct_packet_no_remainder ... ok
test solution_test::test_construct_packet_with_remainder ... ok
test solution_test::test_construct_packet_with_remainder_cyrillic ... ok
test solution_test::test_consuming_packets ... thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"foo "`,
 right: `"foo bar baz"`', tests/solution_test.rs:204:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
FAILED
test solution_test::test_deserialize_invalid_packet ... ok
test solution_test::test_deserialize_packet ... ok
test solution_test::test_deserialize_unicode_packet ... ok
test solution_test::test_full_roundtrip ... thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"foo bar baz"`,
 right: `"foo bar ba"`', tests/solution_test.rs:215:9
FAILED
test solution_test::test_full_roundtrip_for_zero_size_string ... thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidPacket', src/libcore/result.rs:1165:5
FAILED
test solution_test::test_invalid_packet_combination ... thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Utf8Error { valid_up_to: 2, error_len: None }', src/libcore/result.rs:1165:5
FAILED
test solution_test::test_iterating_packets ... ok
test solution_test::test_iterating_packets_for_zero_size_string ... ok
test solution_test::test_serialize_packet ... ok
test solution_test::test_zero_size ... ok

failures:

---- solution_test::test_consuming_packets stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"foo "`,
 right: `"foo bar baz"`', tests/solution_test.rs:197:5

---- solution_test::test_full_roundtrip stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"foo bar baz"`,
 right: `"foo bar ba"`', tests/solution_test.rs:210:5

---- solution_test::test_full_roundtrip_for_zero_size_string stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidPacket', tests/solution_test.rs:221:5

---- solution_test::test_invalid_packet_combination stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Utf8Error { valid_up_to: 2, error_len: None }', tests/solution_test.rs:232:5


failures:
    solution_test::test_consuming_packets
    solution_test::test_full_roundtrip
    solution_test::test_full_roundtrip_for_zero_size_string
    solution_test::test_invalid_packet_combination

test result: FAILED. 11 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (2 версии и 4 коментара)

Стела качи първо решение на 03.12.2019 13:59 (преди почти 6 години)

Стела качи решение на 03.12.2019 14:04 (преди почти 6 години)

use std::fmt;
use std::cmp::min;
use std::convert::TryInto;
/// Грешките, които ще очакваме да върнете. По-долу ще е описано кои от тези грешки очакваме да се
/// върнат в каква ситуация.
///
#[derive(Debug)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
/// Нужна е имплементация на Display за грешките, за да може да имплементират `std::error::Error`.
/// Свободни сте да напишете каквито искате съобщения, ще тестваме само типовете, не низовия им
/// вид.
///
/// Ако са във формат на хайку, няма да получите бонус точки, но може да получите чувство на
/// вътрешно удовлетворение.
///
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: Error has become a thing!", self)
}
}
/// Тази имплементация би трябвало да сработи директно благодарение на горните. При желание, можете
/// да си имплементирате ръчно някои от методите, само внимавайте.
///
impl std::error::Error for PacketError {}
///Помощна функция за пресмятане на checksum
///
pub fn sum_of_bytes(message: &[u8])->u32{
let mut sum: u32 = 0;
for x in message{
sum += (*x) as u32;
}
return sum;
}
///Помощна функция за разделяне на информация на две
///Основната й работа е да реши къде да я раздели - изнасяне на проверката за грешки тук,
///вместо навсякъде, където се наложи
///
pub fn split(source: &[u8], pos: usize) -> (&[u8], &[u8]){
if pos >= source.len() {
let empt: &[u8] = &[];
return (source, empt);
}
return source.split_at(pos);
}
/// Един пакет, съдържащ част от съобщението. Изберете сами какви полета да използвате за
/// съхранение.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
#[derive(PartialEq, Debug)]
pub struct Packet {
version: u8,
size: u8,
message: Vec<u8>,
checksum: u32,
}
impl Packet {
/// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
/// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
/// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
/// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
///
/// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
/// литерал като b"abcd".
///
/// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
/// дължината на `source` (и остатъка ще е празен slice).
///
/// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
/// просто е невалидно, програмистка грешка).
///
pub fn from_source(source: &[u8], size: u8) -> (Self, &[u8]) {
if size == 0{
panic!("Size of package is 0!")
}
let (message, rest) = split(source, size as usize);
let packet = Packet{
version: 1,
size: min(size, source.len() as u8),
message: Vec::from(message),
checksum: sum_of_bytes(message),
};
return (packet, rest);
}
/// Връща само slice-а който пакета опакова. Тоест, ако сме конструирали пакета със
/// `Packet::from_source(b"abc", 3)`, очакваме `.payload()` да ни върне `b"abc"`.
///
/// Защо това просто не е публично property? За да не позволяваме мутация, а само конструиране
/// и четене.
///
pub fn payload(&self) -> &[u8] {
let msg: &[u8] = &self.message;
return msg;
}
/// Сериализира пакета, тоест превръща го в байтове, готови за трансфер. Версия, дължина,
/// съобщение (payload), checksum. Вижте по-горе за детайлно обяснение.
///
pub fn serialize(&self) -> Vec<u8> {
let mut data:Vec<u8> = self.message.clone();
data.insert(0, self.version);
data.insert(1, self.size);
data.append(&mut self.checksum.to_be_bytes().to_vec());
return data;
}
/// Имайки slice от байтове, искаме да извадим един пакет от началото и да върнем остатъка,
/// пакетиран в `Result`.
///
/// Байтовете са репрезентация на пакет -- версия, размер, и т.н. както е описано по-горе.
///
/// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси
/// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
///
/// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`.
///
/// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
/// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
///
/// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
/// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
/// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
/// `PacketError::InvalidChecksum`. Малко тъпо! Но уви, протоколите имат подобни тъпи ръбове,
/// особено като са написани за един уикенд. Авторите обещават по-добър протокол за версия 2.
///
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
if bytes.len() < 8{
return Err(PacketError::InvalidPacket)
}
let version:u8 = bytes[0];
+ if version != 1 {
+ return Err(PacketError::UnknownProtocolVersion);
+ }
+
let size:u8 = bytes[1];
let n:usize = 2 + size as usize;
- if bytes.len() < n || size < 1 || version != 1 {
+ if bytes.len() < n || size < 1 {
return Err(PacketError::InvalidPacket);
}
let message:Vec<u8> = bytes[2..n].to_vec();
if bytes.len() < 4+n {
return Err(PacketError::InvalidPacket);
}
let checkbytes:[u8;4] = bytes[n..n+4].try_into().unwrap();
let checksum:u32 = u32::from_be_bytes(checkbytes);
if checksum != sum_of_bytes(&message) {
return Err(PacketError::InvalidChecksum);
}
let p:Packet = Packet{
version,
size,
message,
checksum,
};
return Ok((p, &bytes[n+4..]));
}
}
/// Структура, която ще служи за итериране по пакети. Ще я конструираме от някакво съобщение, и
/// итерацията ще връща всеки следващ пакет, докато съобщението не бъде напълно "изпратено".
/// Изберете каквито полета ви трябват.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
pub struct PacketSerializer {
packets:Vec<Packet>,
}
impl Iterator for PacketSerializer {
type Item = Packet;
fn next(&mut self) -> Option<Self::Item> {
if !self.packets.is_empty(){
let item:Packet = self.packets.remove(0);
return Some(item);
}
return None;
}
}
/// Този trait ще ни позволи да конвертираме един `String` (а ако искаме, и други неща) от и до
/// комплект от байтове за прехвърляне по мрежата.
///
/// Детайли за методите вижте по-долу в имплементацията на този trait за `String`.
///
pub trait Packetable: Sized {
fn to_packets(&self, packet_size: u8) -> PacketSerializer;
fn to_packet_data(&self, packet_size: u8) -> Vec<u8>;
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError>;
}
impl Packetable for String {
/// Този метод приема размер, който да използваме за размера на payload-а на всеки пакет. Връща
/// итератор върху въпросните пакети. Низа трябва да се използва под формата на байтове.
///
/// Както при `.from_source`, ако подадения `packet_size` е по-голям от дължината на оставащите
/// байтове, приемаме, че размера на съответния пакет ще е колкото остава.
///
fn to_packets(&self, packet_size: u8) -> PacketSerializer {
let size = packet_size as usize;
let mut packets:Vec<Packet> = Vec::new();
let mut rest = self.as_bytes();
while !rest.is_empty(){
let (a, b) = split(rest, size);
let data = std::str::from_utf8(a).unwrap();
rest = b;
let (packet, _) = Packet::deserialize(&(String::from(data).to_packet_data(packet_size))).ok().unwrap();

Няма нужда индивидуалния пакет да има payload, който е валиден низ. Ако изберем произволен packet size, може да се окаже, че сме отрязали низа по средата на някоя буква. Което е ок -- пакетите имат за цел да бъдат транспортен механизъм.

packets.push(packet);
}
return PacketSerializer{packets: packets};
}
/// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
/// от байтове със `.serialize()` и да го натъпчем във вектора.
///
/// Както при `.from_source`, ако подадения `packet_size` е по-голям от дължината на оставащите
/// байтове, приемаме, че размера на съответния пакет ще е колкото остава.
///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let (packet, _) = Packet::from_source(self.as_bytes(), packet_size);
return packet.serialize();
}

Логиката тук не е съвсем правилна. Идеята беше не да сериализираш един пакет, а да разделиш низа на байтове с дадения размер, да пакетираш парчетата в пакети (което можеше да направиш с поредни извиквания на Packet::from_source) и да събереш сериализациите на тези пакети.

Ти си обърнала логиката -- този метод се вика като помощна функция на горния, а имаше логика да е обратното. Не сме се разбрали за условието.

/// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
/// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
/// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
///
/// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
///
/// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
/// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
/// Тогава връщаме `PacketError::CorruptedMessage`.
///
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
- if packet_data.len() < 8 || packet_data[0] != 1 {
+ if packet_data.len() < 8 {
return Err(PacketError::InvalidPacket)
}
+ if packet_data[0] != 1 {
+ return Err(PacketError::UnknownProtocolVersion);
+ }
let size:usize = packet_data[1] as usize;
if size < 1 || packet_data.len() < 6+size {
return Err(PacketError::InvalidPacket);
}
let data:&str = std::str::from_utf8(&packet_data[2..2+size]).unwrap();
- let checkbytes:[u8;4] = packet_data[2+size..6+size].try_into().unwrap();
+ let string:Result<[u8;4], _> = packet_data[2+size..6+size].try_into();
+ if string.is_err(){
+ return Err(PacketError::CorruptedMessage);
+ }
+ let checkbytes:[u8;4] = string.unwrap();
let checksum:u32 = u32::from_be_bytes(checkbytes);
if checksum != sum_of_bytes(data.as_bytes()){
return Err(PacketError::InvalidChecksum);
}
return Ok(data.to_string());
}
}

Кода започва обещаващо, но лека-полека става малко рошав и допускаш грешки в логиката. Подозирам, че късно си почнала да го пишеш -- другия път по-отрано. Разгледай моето решение като пример за очакваната логика.