cfdp_simplified/pdu/
ops.rs

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    /// attempt to construct an ID from a Vec of big endian bytes.
42    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    /// attempt to construct an ID from a Vec of big endian bytes.
59    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    /// Increment the internal counter of the ID. This is useful for sequence numbers.
74    pub fn increment(&mut self) {
75        self.0 = self.0.overflowing_add(1).0;
76    }
77
78    /// Return the current counter value and then increment.
79    pub fn get_and_increment(&mut self) -> Self {
80        let current = self.0;
81        self.increment();
82        EntityID(current)
83    }
84
85    /// Convert the internal counter to Big endian bytes.
86    pub fn to_be_bytes(self) -> Vec<u8> {
87        self.0.to_be_bytes().to_vec()
88    }
89
90    /// convert underlying ID to u64
91    pub fn to_u64(&self) -> u64 {
92        self.0 as u64
93    }
94}
95
96impl TransactionSeqNum {
97    /// Increment the internal counter of the ID. This is useful for sequence numbers.
98    pub fn increment(&mut self) {
99        self.0 = self.0.overflowing_add(1).0;
100    }
101
102    /// Return the current counter value and then increment.
103    pub fn get_and_increment(&mut self) -> Self {
104        let current = self.0;
105        self.increment();
106        TransactionSeqNum::from(current)
107    }
108
109    /// Convert the internal counter to Big endian bytes.
110    pub fn to_be_bytes(self) -> Vec<u8> {
111        self.0.to_be_bytes().to_vec()
112    }
113
114    /// convert underlying ID to u64
115    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    // This trait requires `fmt` with this exact signature.
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        write!(f, "{}", self.to_u64())
179    }
180}
181
182impl fmt::Display for TransactionSeqNum {
183    // This trait requires `fmt` with this exact signature.
184    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)]
191/// The possible directive types of a PDU, used to distinguish the PDUs.
192pub enum PDUDirective {
193    /// End of File PDU
194    EoF = 0x04,
195    /// Metadata PDU
196    Metadata = 0x07,
197    /// Negative Acknowledgement PDU
198    Nak = 0x08,
199}
200
201#[repr(u8)]
202#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)]
203/// Subdirective codes for Positive acknowledgement PDUs.
204pub enum ACKSubDirective {
205    Other = 0b0000,
206    Finished = 0b0001,
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)]
210/// Continuation state of a record.
211pub enum RecordContinuationState {
212    First = 0b01,
213    Last = 0b10,
214    Unsegmented = 0b11,
215    Interim = 0b00,
216}
217
218#[derive(Clone, Debug, PartialEq, Eq)]
219/// Holds File data beginning at the given offset.
220pub struct UnsegmentedFileData {
221    /// Byte offset into the file where this data begins.
222    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)]
257/// A holder for both possible types of File data PDUs.
258pub 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)]
288/// All operations PDUs
289pub 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        //  condition (4 bits + 4 bits spare)
350        //  checksum (4 bytes)
351        //  File_size (FSS)
352        //  Fault Location (0 or TLV of fault location)
353        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        // closure + checksum (1 byte)
407        // file size (FSS)
408        // source filename (1 + len )
409        // destination filename (1 + len)
410        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        // Closure request flag always false
419        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    // 2 x FileSizeSensitive x N length for N requests.
530    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    /// returns the maximum number of nak segments which can fit into one PDU given the payload length
596    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}