1
use crate::cfdp::pdu::{
2
    add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader,
3
};
4
use crate::cfdp::tlv::{
5
    EntityIdTlv, FilestoreResponseTlv, GenericTlv, Tlv, TlvType, TlvTypeField, WritableTlv,
6
};
7
use crate::cfdp::{ConditionCode, CrcFlag, Direction, PduType, TlvLvError};
8
use crate::ByteConversionError;
9
use num_enum::{IntoPrimitive, TryFromPrimitive};
10
#[cfg(feature = "serde")]
11
use serde::{Deserialize, Serialize};
12

            
13
use super::tlv::ReadableTlv;
14
use super::{CfdpPdu, WritablePduPacket};
15

            
16
10
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
17
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19
#[repr(u8)]
20
pub enum DeliveryCode {
21
    Complete = 0,
22
    Incomplete = 1,
23
}
24

            
25
10
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
26
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28
#[repr(u8)]
29
pub enum FileStatus {
30
    DiscardDeliberately = 0b00,
31
    DiscardedFsRejection = 0b01,
32
    Retained = 0b10,
33
    Unreported = 0b11,
34
}
35

            
36
/// Finished PDU abstraction.
37
///
38
/// For more information, refer to CFDP chapter 5.2.3.
39
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41
pub struct FinishedPduCreator<'fs_responses> {
42
    pdu_header: PduHeader,
43
    condition_code: ConditionCode,
44
    delivery_code: DeliveryCode,
45
    file_status: FileStatus,
46
    fs_responses:
47
        &'fs_responses [FilestoreResponseTlv<'fs_responses, 'fs_responses, 'fs_responses>],
48
    fault_location: Option<EntityIdTlv>,
49
}
50

            
51
impl<'fs_responses> FinishedPduCreator<'fs_responses> {
52
    /// Default finished PDU: No error (no fault location field) and no filestore responses.
53
16
    pub fn new_default(
54
16
        pdu_header: PduHeader,
55
16
        delivery_code: DeliveryCode,
56
16
        file_status: FileStatus,
57
16
    ) -> Self {
58
16
        Self::new_generic(
59
16
            pdu_header,
60
16
            ConditionCode::NoError,
61
16
            delivery_code,
62
16
            file_status,
63
16
            &[],
64
16
            None,
65
16
        )
66
16
    }
67

            
68
4
    pub fn new_with_error(
69
4
        pdu_header: PduHeader,
70
4
        condition_code: ConditionCode,
71
4
        delivery_code: DeliveryCode,
72
4
        file_status: FileStatus,
73
4
        fault_location: EntityIdTlv,
74
4
    ) -> Self {
75
4
        Self::new_generic(
76
4
            pdu_header,
77
4
            condition_code,
78
4
            delivery_code,
79
4
            file_status,
80
4
            &[],
81
4
            Some(fault_location),
82
4
        )
83
4
    }
84

            
85
24
    pub fn new_generic(
86
24
        mut pdu_header: PduHeader,
87
24
        condition_code: ConditionCode,
88
24
        delivery_code: DeliveryCode,
89
24
        file_status: FileStatus,
90
24
        fs_responses: &'fs_responses [FilestoreResponseTlv<
91
24
            'fs_responses,
92
24
            'fs_responses,
93
24
            'fs_responses,
94
24
        >],
95
24
        fault_location: Option<EntityIdTlv>,
96
24
    ) -> Self {
97
24
        pdu_header.pdu_type = PduType::FileDirective;
98
24
        // Enforce correct direction bit.
99
24
        pdu_header.pdu_conf.direction = Direction::TowardsSender;
100
24
        let mut finished_pdu = Self {
101
24
            pdu_header,
102
24
            condition_code,
103
24
            delivery_code,
104
24
            file_status,
105
24
            fs_responses,
106
24
            fault_location,
107
24
        };
108
24
        finished_pdu.pdu_header.pdu_datafield_len = finished_pdu.calc_pdu_datafield_len() as u16;
109
24
        finished_pdu
110
24
    }
111

            
112
4
    pub fn condition_code(&self) -> ConditionCode {
113
4
        self.condition_code
114
4
    }
115

            
116
4
    pub fn delivery_code(&self) -> DeliveryCode {
117
4
        self.delivery_code
118
4
    }
119

            
120
4
    pub fn file_status(&self) -> FileStatus {
121
4
        self.file_status
122
4
    }
123

            
124
    // If there are no filestore responses, an empty slice will be returned.
125
12
    pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] {
126
12
        self.fs_responses
127
12
    }
128

            
129
6
    pub fn fault_location(&self) -> Option<EntityIdTlv> {
130
6
        self.fault_location
131
6
    }
132

            
133
66
    fn calc_pdu_datafield_len(&self) -> usize {
134
66
        let mut datafield_len = 2;
135
90
        for fs_response in self.fs_responses {
136
24
            datafield_len += fs_response.len_full();
137
24
        }
138
66
        if let Some(fault_location) = self.fault_location {
139
18
            datafield_len += fault_location.len_full();
140
48
        }
141
66
        if self.crc_flag() == CrcFlag::WithCrc {
142
6
            datafield_len += 2;
143
60
        }
144
66
        datafield_len
145
66
    }
146
}
147

            
148
impl CfdpPdu for FinishedPduCreator<'_> {
149
134
    fn pdu_header(&self) -> &PduHeader {
150
134
        &self.pdu_header
151
134
    }
152

            
153
2
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
154
2
        Some(FileDirectiveType::FinishedPdu)
155
2
    }
156
}
157

            
158
impl WritablePduPacket for FinishedPduCreator<'_> {
159
24
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
160
24
        let expected_len = self.len_written();
161
24
        if buf.len() < expected_len {
162
2
            return Err(ByteConversionError::ToSliceTooSmall {
163
2
                found: buf.len(),
164
2
                expected: expected_len,
165
2
            }
166
2
            .into());
167
22
        }
168

            
169
22
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
170
22
        buf[current_idx] = FileDirectiveType::FinishedPdu as u8;
171
22
        current_idx += 1;
172
22
        buf[current_idx] = ((self.condition_code as u8) << 4)
173
22
            | ((self.delivery_code as u8) << 2)
174
22
            | self.file_status as u8;
175
22
        current_idx += 1;
176
30
        for fs_responses in self.fs_responses {
177
8
            current_idx += fs_responses.write_to_bytes(&mut buf[current_idx..])?;
178
        }
179
22
        if let Some(fault_location) = self.fault_location {
180
6
            current_idx += fault_location.write_to_bytes(&mut buf[current_idx..])?;
181
16
        }
182
22
        if self.crc_flag() == CrcFlag::WithCrc {
183
2
            current_idx = add_pdu_crc(buf, current_idx);
184
20
        }
185
22
        Ok(current_idx)
186
24
    }
187

            
188
42
    fn len_written(&self) -> usize {
189
42
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
190
42
    }
191
}
192

            
193
/// Helper structure to loop through all filestore responses of a read Finished PDU. It should be
194
/// noted that iterators in Rust are not fallible, but the TLV creation can fail, for example if
195
/// the raw TLV data is invalid for some reason. In that case, the iterator will yield [None]
196
/// because there is no way to recover from this.
197
///
198
/// The user can accumulate the length of all TLVs yielded by the iterator and compare it against
199
/// the full length of the options to check whether the iterator was able to parse all TLVs
200
/// successfully.
201
pub struct FilestoreResponseIterator<'buf> {
202
    responses_buf: &'buf [u8],
203
    current_idx: usize,
204
}
205

            
206
impl<'buf> Iterator for FilestoreResponseIterator<'buf> {
207
    type Item = FilestoreResponseTlv<'buf, 'buf, 'buf>;
208

            
209
18
    fn next(&mut self) -> Option<Self::Item> {
210
18
        if self.current_idx == self.responses_buf.len() {
211
10
            return None;
212
8
        }
213
8
        let tlv = FilestoreResponseTlv::from_bytes(&self.responses_buf[self.current_idx..]);
214
8
        // There are not really fallible iterators so we can't continue here..
215
8
        if tlv.is_err() {
216
            return None;
217
8
        }
218
8
        let tlv = tlv.unwrap();
219
8
        self.current_idx += tlv.len_full();
220
8
        Some(tlv)
221
18
    }
222
}
223

            
224
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
225
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
226
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
227
pub struct FinishedPduReader<'buf> {
228
    pdu_header: PduHeader,
229
    condition_code: ConditionCode,
230
    delivery_code: DeliveryCode,
231
    file_status: FileStatus,
232
    fs_responses_raw: &'buf [u8],
233
    fault_location: Option<EntityIdTlv>,
234
}
235

            
236
impl<'buf> FinishedPduReader<'buf> {
237
    /// Generates [Self] from a raw bytestream.
238
4
    pub fn new(buf: &'buf [u8]) -> Result<Self, PduError> {
239
4
        Self::from_bytes(buf)
240
4
    }
241

            
242
    /// Generates [Self] from a raw bytestream.
243
12
    pub fn from_bytes(buf: &'buf [u8]) -> Result<Self, PduError> {
244
12
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
245
12
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
246
10
        let min_expected_len = current_idx + 2;
247
10
        generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
248
10
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
249
            PduError::InvalidDirectiveType {
250
                found: buf[current_idx],
251
                expected: Some(FileDirectiveType::FinishedPdu),
252
            }
253
10
        })?;
254
10
        if directive_type != FileDirectiveType::FinishedPdu {
255
            return Err(PduError::WrongDirectiveType {
256
                found: directive_type,
257
                expected: FileDirectiveType::FinishedPdu,
258
            });
259
10
        }
260
10
        current_idx += 1;
261
10
        let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
262
10
            .map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
263
        // Unwrap is okay here for both of the following operations which can not fail.
264
10
        let delivery_code = DeliveryCode::try_from((buf[current_idx] >> 2) & 0b1).unwrap();
265
10
        let file_status = FileStatus::try_from(buf[current_idx] & 0b11).unwrap();
266
10
        current_idx += 1;
267
10
        let (fs_responses_raw, fault_location) =
268
10
            Self::parse_tlv_fields(current_idx, full_len_without_crc, buf)?;
269
10
        Ok(Self {
270
10
            pdu_header,
271
10
            condition_code,
272
10
            delivery_code,
273
10
            file_status,
274
10
            fs_responses_raw,
275
10
            fault_location,
276
10
        })
277
12
    }
278

            
279
    pub fn fs_responses_raw(&self) -> &[u8] {
280
        self.fs_responses_raw
281
    }
282

            
283
10
    pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> {
284
10
        FilestoreResponseIterator {
285
10
            responses_buf: self.fs_responses_raw,
286
10
            current_idx: 0,
287
10
        }
288
10
    }
289

            
290
2
    pub fn condition_code(&self) -> ConditionCode {
291
2
        self.condition_code
292
2
    }
293

            
294
2
    pub fn delivery_code(&self) -> DeliveryCode {
295
2
        self.delivery_code
296
2
    }
297

            
298
2
    pub fn file_status(&self) -> FileStatus {
299
2
        self.file_status
300
2
    }
301

            
302
2
    pub fn fault_location(&self) -> Option<EntityIdTlv> {
303
2
        self.fault_location
304
2
    }
305

            
306
10
    fn parse_tlv_fields(
307
10
        mut current_idx: usize,
308
10
        full_len_without_crc: usize,
309
10
        buf: &[u8],
310
10
    ) -> Result<(&[u8], Option<EntityIdTlv>), PduError> {
311
10
        let mut fs_responses: &[u8] = &[];
312
10
        let mut fault_location = None;
313
10
        let start_of_fs_responses = current_idx;
314
        // There are leftover filestore response(s) and/or a fault location field.
315
22
        while current_idx < full_len_without_crc {
316
12
            let next_tlv = Tlv::from_bytes(&buf[current_idx..])?;
317
12
            match next_tlv.tlv_type_field() {
318
12
                TlvTypeField::Standard(tlv_type) => {
319
12
                    if tlv_type == TlvType::FilestoreResponse {
320
8
                        current_idx += next_tlv.len_full();
321
8
                        if current_idx == full_len_without_crc {
322
2
                            fs_responses = &buf[start_of_fs_responses..current_idx];
323
6
                        }
324
4
                    } else if tlv_type == TlvType::EntityId {
325
                        // At least one FS response is included.
326
4
                        if current_idx > start_of_fs_responses {
327
2
                            fs_responses = &buf[start_of_fs_responses..current_idx];
328
2
                        }
329
4
                        fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
330
4
                        current_idx += fault_location.as_ref().unwrap().len_full();
331
4
                        // This is considered a configuration error: The entity ID has to be the
332
4
                        // last TLV, everything else would break the whole handling of the packet
333
4
                        // TLVs.
334
4
                        if current_idx != full_len_without_crc {
335
                            return Err(PduError::FormatError);
336
4
                        }
337
                    } else {
338
                        return Err(TlvLvError::InvalidTlvTypeField {
339
                            found: tlv_type.into(),
340
                            expected: Some(TlvType::FilestoreResponse.into()),
341
                        }
342
                        .into());
343
                    }
344
                }
345
                TlvTypeField::Custom(raw) => {
346
                    return Err(TlvLvError::InvalidTlvTypeField {
347
                        found: raw,
348
                        expected: None,
349
                    }
350
                    .into());
351
                }
352
            }
353
        }
354
10
        Ok((fs_responses, fault_location))
355
10
    }
356
}
357

            
358
impl CfdpPdu for FinishedPduReader<'_> {
359
2
    fn pdu_header(&self) -> &PduHeader {
360
2
        &self.pdu_header
361
2
    }
362

            
363
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
364
        Some(FileDirectiveType::FinishedPdu)
365
    }
366
}
367

            
368
impl PartialEq<FinishedPduCreator<'_>> for FinishedPduReader<'_> {
369
10
    fn eq(&self, other: &FinishedPduCreator<'_>) -> bool {
370
10
        self.pdu_header == other.pdu_header
371
10
            && self.condition_code == other.condition_code
372
10
            && self.delivery_code == other.delivery_code
373
10
            && self.file_status == other.file_status
374
10
            && self.fault_location == other.fault_location
375
10
            && self
376
10
                .fs_responses_iter()
377
10
                .zip(other.filestore_responses().iter())
378
14
                .all(|(a, b)| a == *b)
379
10
    }
380
}
381

            
382
impl PartialEq<FinishedPduReader<'_>> for FinishedPduCreator<'_> {
383
6
    fn eq(&self, other: &FinishedPduReader<'_>) -> bool {
384
6
        other.eq(self)
385
6
    }
386
}
387

            
388
#[cfg(test)]
389
mod tests {
390
    use super::*;
391
    use crate::cfdp::lv::Lv;
392
    use crate::cfdp::pdu::tests::{
393
        common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID,
394
    };
395
    use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
396
    use crate::cfdp::tlv::FilestoreResponseTlv;
397
    use crate::cfdp::{ConditionCode, CrcFlag, Direction, LargeFileFlag, TransmissionMode};
398

            
399
16
    fn generic_finished_pdu(
400
16
        crc_flag: CrcFlag,
401
16
        fss: LargeFileFlag,
402
16
        delivery_code: DeliveryCode,
403
16
        file_status: FileStatus,
404
16
    ) -> FinishedPduCreator<'static> {
405
16
        let pdu_header = PduHeader::new_no_file_data(common_pdu_conf(crc_flag, fss), 0);
406
16
        FinishedPduCreator::new_default(pdu_header, delivery_code, file_status)
407
16
    }
408

            
409
    #[test]
410
2
    fn test_basic() {
411
2
        let finished_pdu = generic_finished_pdu(
412
2
            CrcFlag::NoCrc,
413
2
            LargeFileFlag::Normal,
414
2
            DeliveryCode::Complete,
415
2
            FileStatus::Retained,
416
2
        );
417
2
        assert_eq!(finished_pdu.condition_code(), ConditionCode::NoError);
418
2
        assert_eq!(
419
2
            finished_pdu.pdu_header().pdu_conf.direction,
420
2
            Direction::TowardsSender
421
2
        );
422
2
        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
423
2
        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
424
2
        assert_eq!(finished_pdu.filestore_responses(), &[]);
425
2
        assert_eq!(finished_pdu.fault_location(), None);
426
2
        assert_eq!(finished_pdu.pdu_header().pdu_datafield_len, 2);
427

            
428
2
        assert_eq!(finished_pdu.crc_flag(), CrcFlag::NoCrc);
429
2
        assert_eq!(finished_pdu.file_flag(), LargeFileFlag::Normal);
430
2
        assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective);
431
2
        assert_eq!(
432
2
            finished_pdu.file_directive_type(),
433
2
            Some(FileDirectiveType::FinishedPdu)
434
2
        );
435
2
        assert_eq!(
436
2
            finished_pdu.transmission_mode(),
437
2
            TransmissionMode::Acknowledged
438
2
        );
439
2
        assert_eq!(finished_pdu.direction(), Direction::TowardsSender);
440
2
        assert_eq!(finished_pdu.source_id(), TEST_SRC_ID.into());
441
2
        assert_eq!(finished_pdu.dest_id(), TEST_DEST_ID.into());
442
2
        assert_eq!(finished_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
443
2
    }
444

            
445
6
    fn generic_serialization_test_no_error(delivery_code: DeliveryCode, file_status: FileStatus) {
446
6
        let finished_pdu = generic_finished_pdu(
447
6
            CrcFlag::NoCrc,
448
6
            LargeFileFlag::Normal,
449
6
            delivery_code,
450
6
            file_status,
451
6
        );
452
6
        let mut buf: [u8; 64] = [0; 64];
453
6
        let written = finished_pdu.write_to_bytes(&mut buf);
454
6
        assert!(written.is_ok());
455
6
        let written = written.unwrap();
456
6
        assert_eq!(written, 9);
457
6
        assert_eq!(written, finished_pdu.len_written());
458
6
        assert_eq!(written, finished_pdu.pdu_header().header_len() + 2);
459
6
        assert_eq!(
460
6
            finished_pdu.pdu_header().pdu_conf.direction,
461
6
            Direction::TowardsSender
462
6
        );
463
6
        verify_raw_header(finished_pdu.pdu_header(), &buf);
464
6
        let mut current_idx = finished_pdu.pdu_header().header_len();
465
6
        assert_eq!(buf[current_idx], FileDirectiveType::FinishedPdu as u8);
466
6
        current_idx += 1;
467
6
        assert_eq!(
468
6
            (buf[current_idx] >> 4) & 0b1111,
469
6
            ConditionCode::NoError as u8
470
6
        );
471
6
        assert_eq!((buf[current_idx] >> 2) & 0b1, delivery_code as u8);
472
6
        assert_eq!(buf[current_idx] & 0b11, file_status as u8);
473
6
        assert_eq!(current_idx + 1, written);
474
6
    }
475

            
476
    #[test]
477
2
    fn test_serialization_simple() {
478
2
        generic_serialization_test_no_error(DeliveryCode::Complete, FileStatus::Retained);
479
2
    }
480

            
481
    #[test]
482
2
    fn test_serialization_simple_2() {
483
2
        generic_serialization_test_no_error(
484
2
            DeliveryCode::Incomplete,
485
2
            FileStatus::DiscardDeliberately,
486
2
        );
487
2
    }
488

            
489
    #[test]
490
2
    fn test_serialization_simple_3() {
491
2
        generic_serialization_test_no_error(DeliveryCode::Incomplete, FileStatus::Unreported);
492
2
    }
493

            
494
    #[test]
495
2
    fn test_write_to_vec() {
496
2
        let finished_pdu = generic_finished_pdu(
497
2
            CrcFlag::NoCrc,
498
2
            LargeFileFlag::Normal,
499
2
            DeliveryCode::Complete,
500
2
            FileStatus::Retained,
501
2
        );
502
2
        let mut buf: [u8; 64] = [0; 64];
503
2
        let written = finished_pdu.write_to_bytes(&mut buf).unwrap();
504
2
        let pdu_vec = finished_pdu.to_vec().unwrap();
505
2
        assert_eq!(buf[0..written], pdu_vec);
506
2
    }
507

            
508
    #[test]
509
2
    fn test_deserialization_simple() {
510
2
        let finished_pdu = generic_finished_pdu(
511
2
            CrcFlag::NoCrc,
512
2
            LargeFileFlag::Normal,
513
2
            DeliveryCode::Complete,
514
2
            FileStatus::Retained,
515
2
        );
516
2
        let mut buf: [u8; 64] = [0; 64];
517
2
        finished_pdu.write_to_bytes(&mut buf).unwrap();
518
2
        let read_back = FinishedPduReader::from_bytes(&buf);
519
2
        assert!(read_back.is_ok());
520
2
        let read_back = read_back.unwrap();
521
2
        assert_eq!(finished_pdu, read_back);
522
        // Use all getter functions here explicitely once.
523
2
        assert_eq!(finished_pdu.pdu_header(), read_back.pdu_header());
524
2
        assert_eq!(finished_pdu.condition_code(), read_back.condition_code());
525
2
        assert_eq!(finished_pdu.fault_location(), read_back.fault_location());
526
2
        assert_eq!(finished_pdu.file_status(), read_back.file_status());
527
2
        assert_eq!(finished_pdu.delivery_code(), read_back.delivery_code());
528
2
    }
529

            
530
    #[test]
531
2
    fn test_serialization_buf_too_small() {
532
2
        let finished_pdu = generic_finished_pdu(
533
2
            CrcFlag::NoCrc,
534
2
            LargeFileFlag::Normal,
535
2
            DeliveryCode::Complete,
536
2
            FileStatus::Retained,
537
2
        );
538
2
        let mut buf: [u8; 8] = [0; 8];
539
2
        let error = finished_pdu.write_to_bytes(&mut buf);
540
2
        assert!(error.is_err());
541
2
        if let PduError::ByteConversion(ByteConversionError::ToSliceTooSmall { found, expected }) =
542
2
            error.unwrap_err()
543
        {
544
2
            assert_eq!(found, 8);
545
2
            assert_eq!(expected, 9);
546
        } else {
547
            panic!("expected to_slice_too_small error");
548
        }
549
2
    }
550

            
551
    #[test]
552
2
    fn test_with_crc() {
553
2
        let finished_pdu = generic_finished_pdu(
554
2
            CrcFlag::WithCrc,
555
2
            LargeFileFlag::Normal,
556
2
            DeliveryCode::Complete,
557
2
            FileStatus::Retained,
558
2
        );
559
2
        let mut buf: [u8; 64] = [0; 64];
560
2
        let written = finished_pdu.write_to_bytes(&mut buf).unwrap();
561
2
        assert_eq!(written, finished_pdu.len_written());
562
2
        let finished_pdu_from_raw = FinishedPduReader::new(&buf).unwrap();
563
2
        assert_eq!(finished_pdu, finished_pdu_from_raw);
564
2
        buf[written - 1] -= 1;
565
2
        let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16;
566
2
        let error = FinishedPduReader::new(&buf).unwrap_err();
567
2
        if let PduError::ChecksumError(e) = error {
568
2
            assert_eq!(e, crc);
569
        } else {
570
            panic!("expected crc error");
571
        }
572
2
    }
573

            
574
    #[test]
575
2
    fn test_with_fault_location() {
576
2
        let pdu_header =
577
2
            PduHeader::new_no_file_data(common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal), 0);
578
2
        let finished_pdu = FinishedPduCreator::new_with_error(
579
2
            pdu_header,
580
2
            ConditionCode::NakLimitReached,
581
2
            DeliveryCode::Incomplete,
582
2
            FileStatus::DiscardDeliberately,
583
2
            EntityIdTlv::new(TEST_DEST_ID.into()),
584
2
        );
585
2
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
586
2
        assert_eq!(finished_pdu_vec.len(), 12);
587
2
        assert_eq!(finished_pdu_vec[9], TlvType::EntityId.into());
588
2
        assert_eq!(finished_pdu_vec[10], 1);
589
2
        assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value_typed());
590
2
        assert_eq!(
591
2
            finished_pdu.fault_location().unwrap().entity_id(),
592
2
            &TEST_DEST_ID.into()
593
2
        );
594
2
    }
595

            
596
    #[test]
597
2
    fn test_deserialization_with_fault_location() {
598
2
        let pdu_header =
599
2
            PduHeader::new_no_file_data(common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal), 0);
600
2
        let entity_id_tlv = EntityIdTlv::new(TEST_DEST_ID.into());
601
2
        let finished_pdu = FinishedPduCreator::new_with_error(
602
2
            pdu_header,
603
2
            ConditionCode::NakLimitReached,
604
2
            DeliveryCode::Incomplete,
605
2
            FileStatus::DiscardDeliberately,
606
2
            entity_id_tlv,
607
2
        );
608
2
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
609
2
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
610
2
        assert_eq!(finished_pdu, finished_pdu_deserialized);
611
2
    }
612

            
613
    #[test]
614
2
    fn test_deserialization_with_fs_responses() {
615
2
        let entity_id_tlv = EntityIdTlv::new(TEST_DEST_ID.into());
616
2
        let first_name = "first.txt";
617
2
        let first_name_lv = Lv::new_from_str(first_name).unwrap();
618
2
        let fs_response_0 = FilestoreResponseTlv::new_no_filestore_message(
619
2
            crate::cfdp::tlv::FilestoreActionCode::CreateFile,
620
2
            0,
621
2
            first_name_lv,
622
2
            None,
623
2
        )
624
2
        .unwrap();
625
2
        let fs_response_1 = FilestoreResponseTlv::new_no_filestore_message(
626
2
            crate::cfdp::tlv::FilestoreActionCode::DeleteFile,
627
2
            0,
628
2
            first_name_lv,
629
2
            None,
630
2
        )
631
2
        .unwrap();
632
2
        let fs_responses = &[fs_response_0, fs_response_1];
633
2

            
634
2
        let pdu_header =
635
2
            PduHeader::new_no_file_data(common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal), 0);
636
2
        let finished_pdu = FinishedPduCreator::new_generic(
637
2
            pdu_header,
638
2
            ConditionCode::NakLimitReached,
639
2
            DeliveryCode::Incomplete,
640
2
            FileStatus::DiscardDeliberately,
641
2
            fs_responses,
642
2
            Some(entity_id_tlv),
643
2
        );
644
2
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
645
2
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
646
2
        assert_eq!(finished_pdu_deserialized, finished_pdu);
647
2
    }
648

            
649
    #[test]
650
2
    fn test_deserialization_with_fs_responses_and_fault_location() {
651
2
        let first_name = "first.txt";
652
2
        let first_name_lv = Lv::new_from_str(first_name).unwrap();
653
2
        let fs_response_0 = FilestoreResponseTlv::new_no_filestore_message(
654
2
            crate::cfdp::tlv::FilestoreActionCode::CreateFile,
655
2
            0,
656
2
            first_name_lv,
657
2
            None,
658
2
        )
659
2
        .unwrap();
660
2
        let fs_response_1 = FilestoreResponseTlv::new_no_filestore_message(
661
2
            crate::cfdp::tlv::FilestoreActionCode::DeleteFile,
662
2
            0,
663
2
            first_name_lv,
664
2
            None,
665
2
        )
666
2
        .unwrap();
667
2
        let fs_responses = &[fs_response_0, fs_response_1];
668
2

            
669
2
        let pdu_header =
670
2
            PduHeader::new_no_file_data(common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal), 0);
671
2
        let finished_pdu = FinishedPduCreator::new_generic(
672
2
            pdu_header,
673
2
            ConditionCode::NakLimitReached,
674
2
            DeliveryCode::Incomplete,
675
2
            FileStatus::DiscardDeliberately,
676
2
            fs_responses,
677
2
            None,
678
2
        );
679
2
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
680
2
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
681
2
        assert_eq!(finished_pdu_deserialized, finished_pdu);
682
2
    }
683
}