1use byteorder::{BigEndian, ReadBytesExt};
2use camino::Utf8PathBuf;
3use num_derive::FromPrimitive;
4use num_traits::FromPrimitive;
5use std::{
6 fmt::{self},
7 io::Read,
8};
9
10use super::{
11 error::{PDUError, PDUResult},
12 header::{
13 read_length_value_pair, Condition, FSSEncode, FileSizeFlag, PDUEncode, SegmentEncode,
14 SegmentedData,
15 },
16};
17use crate::filestore::ChecksumType;
18
19#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
20
21pub struct EntityID(pub u16);
22
23#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
24pub struct TransactionSeqNum(pub u32);
25
26impl From<u16> for EntityID {
27 fn from(value: u16) -> Self {
28 EntityID(value)
29 }
30}
31
32impl From<u32> for TransactionSeqNum {
33 fn from(value: u32) -> Self {
34 TransactionSeqNum(value)
35 }
36}
37
38impl TryFrom<Vec<u8>> for EntityID {
39 type Error = PDUError;
40
41 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
43 let length = value.len();
44 match length {
45 2 => Ok(EntityID(u16::from_be_bytes(
46 value
47 .try_into()
48 .expect("Unable to coerce vec into same sized array."),
49 ))),
50 other => Err(PDUError::UnknownIDLength(other as u8)),
51 }
52 }
53}
54
55impl TryFrom<Vec<u8>> for TransactionSeqNum {
56 type Error = PDUError;
57
58 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
60 let length = value.len();
61 match length {
62 4 => Ok(TransactionSeqNum(u32::from_be_bytes(
63 value
64 .try_into()
65 .expect("Unable to coerce vec into same sized array."),
66 ))),
67 other => Err(PDUError::UnknownIDLength(other as u8)),
68 }
69 }
70}
71
72impl EntityID {
73 pub fn increment(&mut self) {
75 self.0 = self.0.overflowing_add(1).0;
76 }
77
78 pub fn get_and_increment(&mut self) -> Self {
80 let current = self.0;
81 self.increment();
82 EntityID(current)
83 }
84
85 pub fn to_be_bytes(self) -> Vec<u8> {
87 self.0.to_be_bytes().to_vec()
88 }
89
90 pub fn to_u64(&self) -> u64 {
92 self.0 as u64
93 }
94}
95
96impl TransactionSeqNum {
97 pub fn increment(&mut self) {
99 self.0 = self.0.overflowing_add(1).0;
100 }
101
102 pub fn get_and_increment(&mut self) -> Self {
104 let current = self.0;
105 self.increment();
106 TransactionSeqNum::from(current)
107 }
108
109 pub fn to_be_bytes(self) -> Vec<u8> {
111 self.0.to_be_bytes().to_vec()
112 }
113
114 pub fn to_u64(&self) -> u64 {
116 self.0 as u64
117 }
118}
119
120impl PDUEncode for EntityID {
121 type PDUType = Self;
122
123 fn encoded_len(&self) -> u16 {
124 std::mem::size_of::<u16>() as u16
125 }
126
127 fn encode(self) -> Vec<u8> {
128 let mut buffer = vec![self.encoded_len() as u8 - 1_u8];
129 buffer.extend(self.to_be_bytes());
130 buffer
131 }
132
133 fn decode<T: Read>(buffer: &mut T) -> PDUResult<Self::PDUType> {
134 let mut u8_buff = [0u8; 1];
135 buffer.read_exact(&mut u8_buff)?;
136
137 let length: u8 = u8_buff[0] + 1;
138 let mut id = vec![0u8; length as usize];
139 buffer.read_exact(id.as_mut_slice())?;
140 Ok(EntityID(u16::from_be_bytes(
141 id.try_into()
142 .expect("Unable to coerce vec into same sized array."),
143 )))
144 }
145}
146
147impl PDUEncode for TransactionSeqNum {
148 type PDUType = Self;
149
150 fn encoded_len(&self) -> u16 {
151 std::mem::size_of::<u32>() as u16
152 }
153
154 fn encode(self) -> Vec<u8> {
155 let mut buffer = vec![self.encoded_len() as u8 - 1_u8];
156
157 buffer.extend(self.to_be_bytes());
158 buffer
159 }
160
161 fn decode<T: Read>(buffer: &mut T) -> PDUResult<Self::PDUType> {
162 let mut u8_buff = [0u8; 1];
163 buffer.read_exact(&mut u8_buff)?;
164
165 let length: u8 = u8_buff[0] + 1;
166 let mut id = vec![0u8; length as usize];
167 buffer.read_exact(id.as_mut_slice())?;
168 Ok(TransactionSeqNum(u32::from_be_bytes(
169 id.try_into()
170 .expect("Unable to coerce vec into same sized array."),
171 )))
172 }
173}
174
175impl fmt::Display for EntityID {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 write!(f, "{}", self.to_u64())
179 }
180}
181
182impl fmt::Display for TransactionSeqNum {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 write!(f, "{}", self.to_u64())
186 }
187}
188
189#[repr(u8)]
190#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)]
191pub enum PDUDirective {
193 EoF = 0x04,
195 Metadata = 0x07,
197 Nak = 0x08,
199}
200
201#[repr(u8)]
202#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)]
203pub enum ACKSubDirective {
205 Other = 0b0000,
206 Finished = 0b0001,
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)]
210pub enum RecordContinuationState {
212 First = 0b01,
213 Last = 0b10,
214 Unsegmented = 0b11,
215 Interim = 0b00,
216}
217
218#[derive(Clone, Debug, PartialEq, Eq)]
219pub struct UnsegmentedFileData {
221 pub offset: u64,
223 pub file_data: Vec<u8>,
224}
225impl FSSEncode for UnsegmentedFileData {
226 type PDUType = Self;
227
228 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
229 self.file_data.len() as u16 + file_size_flag.encoded_len()
230 }
231
232 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
233 let mut buffer = match file_size_flag {
234 FileSizeFlag::Small => (self.offset as u32).to_be_bytes().to_vec(),
235 FileSizeFlag::Large => self.offset.to_be_bytes().to_vec(),
236 };
237 buffer.extend(self.file_data);
238 buffer
239 }
240
241 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
242 let offset = match file_size_flag {
243 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
244 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
245 };
246
247 let file_data: Vec<u8> = {
248 let mut data: Vec<u8> = vec![];
249 buffer.read_to_end(&mut data)?;
250 data
251 };
252 Ok(Self { offset, file_data })
253 }
254}
255
256#[derive(Clone, Debug, PartialEq, Eq)]
257pub struct FileDataPDU(pub UnsegmentedFileData);
259impl SegmentEncode for FileDataPDU {
260 type PDUType = Self;
261
262 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
263 self.0.encoded_len(file_size_flag)
264 }
265
266 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
267 self.0.encode(file_size_flag)
268 }
269
270 fn decode<T: Read>(
271 buffer: &mut T,
272 segmentation_flag: SegmentedData,
273 file_size_flag: FileSizeFlag,
274 ) -> PDUResult<Self::PDUType> {
275 match segmentation_flag {
276 SegmentedData::Present => Err(PDUError::Unsupported(
277 "Segmented packets not supported".to_string(),
278 )),
279 SegmentedData::NotPresent => Ok(FileDataPDU(UnsegmentedFileData::decode(
280 buffer,
281 file_size_flag,
282 )?)),
283 }
284 }
285}
286
287#[derive(Clone, Debug, PartialEq, Eq)]
288pub enum Operations {
290 EoF(EndOfFile),
291 Metadata(MetadataPDU),
292 Nak(NakPDU),
293}
294impl Operations {
295 pub fn get_directive(&self) -> PDUDirective {
296 match self {
297 Self::EoF(_) => PDUDirective::EoF,
298 Self::Metadata(_) => PDUDirective::Metadata,
299 Self::Nak(_) => PDUDirective::Nak,
300 }
301 }
302}
303impl FSSEncode for Operations {
304 type PDUType = Self;
305
306 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
307 1 + match self {
308 Self::EoF(eof) => eof.encoded_len(file_size_flag),
309 Self::Metadata(metadata) => metadata.encoded_len(file_size_flag),
310 Self::Nak(nak) => nak.encoded_len(file_size_flag),
311 }
312 }
313
314 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
315 let mut buffer: Vec<u8> = vec![self.get_directive() as u8];
316 let message = match self {
317 Self::EoF(msg) => msg.encode(file_size_flag),
318 Self::Metadata(msg) => msg.encode(file_size_flag),
319 Self::Nak(msg) => msg.encode(file_size_flag),
320 };
321 buffer.extend(message);
322 buffer
323 }
324
325 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
326 let mut u8_buff = [0_u8; 1];
327 buffer.read_exact(&mut u8_buff)?;
328
329 match PDUDirective::from_u8(u8_buff[0]).ok_or(PDUError::InvalidDirective(u8_buff[0]))? {
330 PDUDirective::EoF => Ok(Self::EoF(EndOfFile::decode(buffer, file_size_flag)?)),
331 PDUDirective::Metadata => {
332 Ok(Self::Metadata(MetadataPDU::decode(buffer, file_size_flag)?))
333 }
334 PDUDirective::Nak => Ok(Self::Nak(NakPDU::decode(buffer, file_size_flag)?)),
335 }
336 }
337}
338
339#[derive(Clone, Debug, PartialEq, Eq)]
340pub struct EndOfFile {
341 pub condition: Condition,
342 pub checksum: u32,
343 pub file_size: u64,
344}
345impl FSSEncode for EndOfFile {
346 type PDUType = Self;
347
348 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
349 5 + file_size_flag.encoded_len()
354 }
355
356 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
357 let first_byte = (self.condition as u8) << 4;
358 let mut buffer = vec![first_byte];
359
360 buffer.extend(self.checksum.to_be_bytes());
361 match file_size_flag {
362 FileSizeFlag::Small => buffer.extend((self.file_size as u32).to_be_bytes()),
363 FileSizeFlag::Large => buffer.extend(self.file_size.to_be_bytes()),
364 };
365 buffer
366 }
367
368 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
369 let mut u8_buff = [0_u8; 1];
370 buffer.read_exact(&mut u8_buff)?;
371 let condition = {
372 let possible_conditon = (u8_buff[0] & 0xF0) >> 4;
373 Condition::from_u8(possible_conditon)
374 .ok_or(PDUError::InvalidCondition(possible_conditon))?
375 };
376 let checksum = {
377 let mut u32_buff = [0_u8; 4];
378 buffer.read_exact(&mut u32_buff)?;
379 u32::from_be_bytes(u32_buff)
380 };
381
382 let file_size = match file_size_flag {
383 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
384 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
385 };
386
387 Ok(Self {
388 condition,
389 checksum,
390 file_size,
391 })
392 }
393}
394
395#[derive(Clone, Debug, PartialEq, Eq)]
396pub struct MetadataPDU {
397 pub checksum_type: ChecksumType,
398 pub file_size: u64,
399 pub source_filename: Utf8PathBuf,
400 pub destination_filename: Utf8PathBuf,
401}
402impl FSSEncode for MetadataPDU {
403 type PDUType = Self;
404
405 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
406 1 + file_size_flag.encoded_len()
411 + 1
412 + self.source_filename.as_str().len() as u16
413 + 1
414 + self.destination_filename.as_str().len() as u16
415 }
416
417 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
418 let first_byte = ((false as u8) << 6) | (self.checksum_type as u8);
420 let mut buffer = vec![first_byte];
421 match file_size_flag {
422 FileSizeFlag::Small => buffer.extend((self.file_size as u32).to_be_bytes()),
423 FileSizeFlag::Large => buffer.extend(self.file_size.to_be_bytes()),
424 };
425
426 let source_name = self.source_filename.as_str().as_bytes();
427 buffer.push(source_name.len() as u8);
428 buffer.extend(source_name);
429
430 let destination_name = self.destination_filename.as_str().as_bytes();
431 buffer.push(destination_name.len() as u8);
432 buffer.extend(destination_name);
433
434 buffer
435 }
436
437 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
438 let mut u8_buff = [0_u8; 1];
439 buffer.read_exact(&mut u8_buff)?;
440 let first_byte = u8_buff[0];
441 let checksum_type = {
442 let possible = first_byte & 0xF;
443 ChecksumType::from_u8(possible).ok_or(PDUError::InvalidChecksumType(possible))?
444 };
445
446 let file_size = match file_size_flag {
447 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
448 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
449 };
450
451 let source_filename =
452 Utf8PathBuf::from(String::from_utf8(read_length_value_pair(buffer)?)?);
453
454 let destination_filename =
455 Utf8PathBuf::from(String::from_utf8(read_length_value_pair(buffer)?)?);
456
457 Ok(Self {
458 checksum_type,
459 file_size,
460 source_filename,
461 destination_filename,
462 })
463 }
464}
465
466#[derive(Clone, Debug, PartialEq, Eq, Hash)]
467pub struct SegmentRequestForm {
468 pub start_offset: u64,
469 pub end_offset: u64,
470}
471impl From<(u32, u32)> for SegmentRequestForm {
472 fn from(vals: (u32, u32)) -> Self {
473 Self {
474 start_offset: vals.0.into(),
475 end_offset: vals.1.into(),
476 }
477 }
478}
479impl From<(u64, u64)> for SegmentRequestForm {
480 fn from(vals: (u64, u64)) -> Self {
481 Self {
482 start_offset: vals.0,
483 end_offset: vals.1,
484 }
485 }
486}
487impl FSSEncode for SegmentRequestForm {
488 type PDUType = Self;
489
490 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
491 2 * file_size_flag.encoded_len()
492 }
493
494 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
495 match file_size_flag {
496 FileSizeFlag::Small => {
497 let mut buffer = (self.start_offset as u32).to_be_bytes().to_vec();
498 buffer.extend((self.end_offset as u32).to_be_bytes());
499 buffer
500 }
501 FileSizeFlag::Large => {
502 let mut buffer = self.start_offset.to_be_bytes().to_vec();
503 buffer.extend(self.end_offset.to_be_bytes());
504 buffer
505 }
506 }
507 }
508
509 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
510 let start_offset = match file_size_flag {
511 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
512 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
513 };
514 let end_offset = match file_size_flag {
515 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
516 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
517 };
518 Ok(Self {
519 start_offset,
520 end_offset,
521 })
522 }
523}
524
525#[derive(Clone, Debug, PartialEq, Eq)]
526pub struct NegativeAcknowledgmentPDU {
527 pub start_of_scope: u64,
528 pub end_of_scope: u64,
529 pub segment_requests: Vec<SegmentRequestForm>,
531}
532pub(crate) type NakPDU = NegativeAcknowledgmentPDU;
533impl FSSEncode for NegativeAcknowledgmentPDU {
534 type PDUType = Self;
535
536 fn encoded_len(&self, file_size_flag: FileSizeFlag) -> u16 {
537 self.segment_requests
538 .iter()
539 .fold(0, |acc, seg| acc + seg.encoded_len(file_size_flag))
540 + 2 * file_size_flag.encoded_len()
541 }
542
543 fn encode(self, file_size_flag: FileSizeFlag) -> Vec<u8> {
544 let mut buffer = match file_size_flag {
545 FileSizeFlag::Small => {
546 let mut buffer = (self.start_of_scope as u32).to_be_bytes().to_vec();
547 buffer.extend((self.end_of_scope as u32).to_be_bytes());
548 buffer
549 }
550 FileSizeFlag::Large => {
551 let mut buffer = self.start_of_scope.to_be_bytes().to_vec();
552 buffer.extend(self.end_of_scope.to_be_bytes());
553 buffer
554 }
555 };
556 self.segment_requests
557 .into_iter()
558 .for_each(|req| buffer.extend(req.encode(file_size_flag)));
559
560 buffer
561 }
562
563 fn decode<T: Read>(buffer: &mut T, file_size_flag: FileSizeFlag) -> PDUResult<Self::PDUType> {
564 let start_of_scope = match file_size_flag {
565 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
566 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
567 };
568 let end_of_scope = match file_size_flag {
569 FileSizeFlag::Large => buffer.read_u64::<BigEndian>()?,
570 FileSizeFlag::Small => buffer.read_u32::<BigEndian>()? as u64,
571 };
572
573 let mut segment_requests = vec![];
574
575 let mut remaining_vec = vec![];
576 buffer.read_to_end(&mut remaining_vec)?;
577 let remaining_buffer = &mut remaining_vec.as_slice();
578
579 while !remaining_buffer.is_empty() {
580 segment_requests.push(SegmentRequestForm::decode(
581 remaining_buffer,
582 file_size_flag,
583 )?)
584 }
585
586 Ok(Self {
587 start_of_scope,
588 end_of_scope,
589 segment_requests,
590 })
591 }
592}
593
594impl NegativeAcknowledgmentPDU {
595 pub fn max_nak_num(file_size_flag: FileSizeFlag, payload_len: u32) -> u32 {
597 (payload_len - 2 * file_size_flag.encoded_len() as u32)
598 / (2 * file_size_flag.encoded_len() as u32)
599 }
600}
601
602#[cfg(test)]
603mod test {
604 use std::u16;
605
606 use super::*;
607
608 use rstest::rstest;
609
610 #[rstest]
611 #[case(EntityID(1_u16), EntityID(2_u16))]
612 fn increment_entity_id(#[case] id: EntityID, #[case] expected: EntityID) {
613 let mut id = id;
614 id.increment();
615 assert_eq!(expected, id)
616 }
617
618 #[rstest]
619 #[case(TransactionSeqNum::from(1_u32), TransactionSeqNum::from(2_u32))]
620 fn increment_seq_num(#[case] id: TransactionSeqNum, #[case] expected: TransactionSeqNum) {
621 let mut id = id;
622 id.increment();
623 assert_eq!(expected, id)
624 }
625
626 #[test]
627 fn get_and_increment_entity_id() {
628 let mut id = EntityID(123_u16);
629 let id2 = id.get_and_increment();
630 assert_eq!(EntityID(123_u16), id2);
631 assert_eq!(EntityID(124_u16), id);
632 }
633
634 #[test]
635 fn get_and_increment_seq_num() {
636 let mut id = TransactionSeqNum::from(123_u32);
637 let id2 = id.get_and_increment();
638 assert_eq!(TransactionSeqNum::from(123_u32), id2);
639 assert_eq!(TransactionSeqNum::from(124_u32), id);
640 }
641
642 #[rstest]
643 fn entity_id_encode(#[values(EntityID(1_u16))] id: EntityID) {
644 let buff = id.encode();
645 let recovered = EntityID::decode(&mut buff.as_slice()).expect("Unable to decode EntityID");
646 assert_eq!(id, recovered)
647 }
648
649 #[rstest]
650 fn seq_num_encode(#[values(TransactionSeqNum::from(1_u32))] id: TransactionSeqNum) {
651 let buff = id.encode();
652 let recovered = TransactionSeqNum::decode(&mut buff.as_slice())
653 .expect("Unable to decode TransactionSeqNum");
654 assert_eq!(id, recovered)
655 }
656
657 #[rstest]
658 #[case(
659 FileDataPDU(UnsegmentedFileData{
660 offset: 34574292984_u64,
661 file_data: (0..255).step_by(3).collect::<Vec<u8>>()
662 })
663 )]
664 #[case(
665 FileDataPDU(UnsegmentedFileData{
666 offset: 12357_u64,
667 file_data: (0..255).step_by(2).collect::<Vec<u8>>()
668 })
669 )]
670 fn unsgemented_data(#[case] expected: FileDataPDU) {
671 let file_size_flag = match &expected {
672 FileDataPDU(UnsegmentedFileData { offset: val, .. }) if val <= &u32::MAX.into() => {
673 FileSizeFlag::Small
674 }
675 _ => FileSizeFlag::Large,
676 };
677
678 let bytes = expected.clone().encode(file_size_flag);
679
680 let recovered = FileDataPDU::decode(
681 &mut bytes.as_slice(),
682 SegmentedData::NotPresent,
683 file_size_flag,
684 )
685 .unwrap();
686
687 assert_eq!(expected, recovered)
688 }
689
690 #[rstest]
691 fn end_of_file_no_error(#[values(7573910375_u64, 194885483_u64)] file_size: u64) {
692 let file_size_flag = match file_size <= u32::MAX.into() {
693 false => FileSizeFlag::Large,
694 true => FileSizeFlag::Small,
695 };
696
697 let end_of_file = EndOfFile {
698 condition: Condition::NoError,
699 checksum: 7580274_u32,
700 file_size,
701 };
702 let expected = Operations::EoF(end_of_file);
703
704 let buffer = expected.clone().encode(file_size_flag);
705
706 let recovered = Operations::decode(&mut buffer.as_slice(), file_size_flag).unwrap();
707
708 assert_eq!(expected, recovered)
709 }
710
711 #[rstest]
712 fn end_of_file_with_error(
713 #[values(
714 Condition::PositiveLimitReached,
715 Condition::InvalidTransmissionMode,
716 Condition::FileStoreRejection,
717 Condition::FileChecksumFailure,
718 Condition::FilesizeError,
719 Condition::NakLimitReached
720 )]
721 condition: Condition,
722 #[values(7573910375_u64, 194885483_u64)] file_size: u64,
723 #[values(EntityID(0u16), EntityID(18484u16), EntityID(u16::MAX))] _entity: EntityID,
724 ) -> Result<(), Box<dyn std::error::Error>> {
725 let file_size_flag = match file_size <= u32::MAX.into() {
726 false => FileSizeFlag::Large,
727 true => FileSizeFlag::Small,
728 };
729
730 let end_of_file = EndOfFile {
731 condition,
732 checksum: 857583994u32,
733 file_size,
734 };
735 let expected = Operations::EoF(end_of_file);
736
737 let buffer = expected.clone().encode(file_size_flag);
738
739 let recovered = Operations::decode(&mut buffer.as_slice(), file_size_flag)?;
740
741 assert_eq!(expected, recovered);
742 Ok(())
743 }
744
745 #[rstest]
746 fn metadata_pdu(
747 #[values(ChecksumType::Null, ChecksumType::Modular)] checksum_type: ChecksumType,
748 #[values(184574_u64, 7574839485_u64)] file_size: u64,
749 ) -> Result<(), Box<dyn std::error::Error>> {
750 let file_size_flag = match file_size <= u32::MAX.into() {
751 true => FileSizeFlag::Small,
752 false => FileSizeFlag::Large,
753 };
754
755 let expected = Operations::Metadata(MetadataPDU {
756 checksum_type,
757 file_size,
758 source_filename: "/the/source/filename.txt".into(),
759 destination_filename: "/the/destination/filename.dat".into(),
760 });
761 let buffer = expected.clone().encode(file_size_flag);
762 let recovered = Operations::decode(&mut buffer.as_slice(), file_size_flag)?;
763
764 assert_eq!(expected, recovered);
765 Ok(())
766 }
767
768 #[rstest]
769 #[case(
770 124_u64,
771 412_u64,
772 vec![
773 SegmentRequestForm{
774 start_offset: 124_u64,
775 end_offset: 204_u64
776 },
777 SegmentRequestForm{
778 start_offset: 312_u64,
779 end_offset: 412_u64
780 },
781 ]
782 )]
783 #[case(
784 32_u64,
785 582872_u64,
786 vec![
787 SegmentRequestForm{
788 start_offset: 32_u64,
789 end_offset: 816_u64
790 },
791 SegmentRequestForm{
792 start_offset: 1024_u64,
793 end_offset: 1536_u64
794 },
795 SegmentRequestForm{
796 start_offset: 582360_u64,
797 end_offset: 582872_u64
798 },
799 ]
800 )]
801 fn nak_pdu(
802 #[case] start_of_scope: u64,
803 #[case] end_of_scope: u64,
804 #[case] segment_requests: Vec<SegmentRequestForm>,
805 #[values(FileSizeFlag::Small, FileSizeFlag::Large)] file_size_flag: FileSizeFlag,
806 ) -> Result<(), Box<dyn std::error::Error>> {
807 let expected = Operations::Nak(NakPDU {
808 start_of_scope,
809 end_of_scope,
810 segment_requests,
811 });
812 let buffer = expected.clone().encode(file_size_flag);
813 let recovered = Operations::decode(&mut buffer.as_slice(), file_size_flag)?;
814
815 assert_eq!(expected, recovered);
816 Ok(())
817 }
818}