1
#[cfg(feature = "alloc")]
2
use super::tlv::TlvOwned;
3
use crate::cfdp::lv::Lv;
4
use crate::cfdp::pdu::{
5
    add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
6
    FileDirectiveType, PduError, PduHeader,
7
};
8
use crate::cfdp::tlv::{Tlv, WritableTlv};
9
use crate::cfdp::{ChecksumType, CrcFlag, Direction, LargeFileFlag, PduType};
10
use crate::ByteConversionError;
11
#[cfg(feature = "alloc")]
12
use alloc::vec::Vec;
13
#[cfg(feature = "serde")]
14
use serde::{Deserialize, Serialize};
15

            
16
use super::tlv::ReadableTlv;
17
use super::{CfdpPdu, WritablePduPacket};
18

            
19
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
20
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22
pub struct MetadataGenericParams {
23
    pub closure_requested: bool,
24
    pub checksum_type: ChecksumType,
25
    pub file_size: u64,
26
}
27

            
28
impl MetadataGenericParams {
29
24
    pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self {
30
24
        Self {
31
24
            closure_requested,
32
24
            checksum_type,
33
24
            file_size,
34
24
        }
35
24
    }
36
}
37

            
38
6
pub fn build_metadata_opts_from_slice(
39
6
    buf: &mut [u8],
40
6
    tlvs: &[Tlv],
41
6
) -> Result<usize, ByteConversionError> {
42
6
    let mut written = 0;
43
18
    for tlv in tlvs {
44
12
        written += tlv.write_to_bytes(&mut buf[written..])?;
45
    }
46
6
    Ok(written)
47
6
}
48

            
49
#[cfg(feature = "alloc")]
50
2
pub fn build_metadata_opts_from_vec(
51
2
    buf: &mut [u8],
52
2
    tlvs: &Vec<Tlv>,
53
2
) -> Result<usize, ByteConversionError> {
54
2
    build_metadata_opts_from_slice(buf, tlvs.as_slice())
55
2
}
56

            
57
#[cfg(feature = "alloc")]
58
pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec<u8> {
59
    let mut sum_vec = Vec::new();
60
    for tlv in tlvs {
61
        sum_vec.extend(tlv.to_vec());
62
    }
63
    sum_vec
64
}
65

            
66
/// Metadata PDU creator abstraction.
67
///
68
/// This abstraction exposes a specialized API for creating metadata PDUs as specified in
69
/// CFDP chapter 5.2.5.
70
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
71
pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> {
72
    pdu_header: PduHeader,
73
    metadata_params: MetadataGenericParams,
74
    src_file_name: Lv<'src_name>,
75
    dest_file_name: Lv<'dest_name>,
76
    options: &'opts [u8],
77
}
78

            
79
impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> {
80
2
    pub fn new_no_opts(
81
2
        pdu_header: PduHeader,
82
2
        metadata_params: MetadataGenericParams,
83
2
        src_file_name: Lv<'src_name>,
84
2
        dest_file_name: Lv<'dest_name>,
85
2
    ) -> Self {
86
2
        Self::new(
87
2
            pdu_header,
88
2
            metadata_params,
89
2
            src_file_name,
90
2
            dest_file_name,
91
2
            &[],
92
2
        )
93
2
    }
94

            
95
    pub fn new_with_opts(
96
        pdu_header: PduHeader,
97
        metadata_params: MetadataGenericParams,
98
        src_file_name: Lv<'src_name>,
99
        dest_file_name: Lv<'dest_name>,
100
        options: &'opts [u8],
101
    ) -> Self {
102
        Self::new(
103
            pdu_header,
104
            metadata_params,
105
            src_file_name,
106
            dest_file_name,
107
            options,
108
        )
109
    }
110

            
111
24
    pub fn new(
112
24
        mut pdu_header: PduHeader,
113
24
        metadata_params: MetadataGenericParams,
114
24
        src_file_name: Lv<'src_name>,
115
24
        dest_file_name: Lv<'dest_name>,
116
24
        options: &'opts [u8],
117
24
    ) -> Self {
118
24
        pdu_header.pdu_type = PduType::FileDirective;
119
24
        pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
120
24
        let mut pdu = Self {
121
24
            pdu_header,
122
24
            metadata_params,
123
24
            src_file_name,
124
24
            dest_file_name,
125
24
            options,
126
24
        };
127
24
        pdu.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16;
128
24
        pdu
129
24
    }
130

            
131
14
    pub fn metadata_params(&self) -> &MetadataGenericParams {
132
14
        &self.metadata_params
133
14
    }
134

            
135
12
    pub fn src_file_name(&self) -> Lv<'src_name> {
136
12
        self.src_file_name
137
12
    }
138

            
139
12
    pub fn dest_file_name(&self) -> Lv<'dest_name> {
140
12
        self.dest_file_name
141
12
    }
142

            
143
58
    pub fn options(&self) -> &'opts [u8] {
144
58
        self.options
145
58
    }
146

            
147
    /// Yield an iterator which can be used to loop through all options. Returns [None] if the
148
    /// options field is empty.
149
10
    pub fn options_iter(&self) -> OptionsIter<'_> {
150
10
        OptionsIter {
151
10
            opt_buf: self.options,
152
10
            current_idx: 0,
153
10
        }
154
10
    }
155

            
156
56
    fn calc_pdu_datafield_len(&self) -> usize {
157
56
        // One directve type octet and one byte of the directive parameter field.
158
56
        let mut len = 2;
159
56
        if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
160
16
            len += 8;
161
40
        } else {
162
40
            len += 4;
163
40
        }
164
56
        len += self.src_file_name.len_full();
165
56
        len += self.dest_file_name.len_full();
166
56
        len += self.options().len();
167
56
        if self.crc_flag() == CrcFlag::WithCrc {
168
6
            len += 2;
169
50
        }
170
56
        len
171
56
    }
172
}
173

            
174
impl CfdpPdu for MetadataPduCreator<'_, '_, '_> {
175
108
    fn pdu_header(&self) -> &PduHeader {
176
108
        &self.pdu_header
177
108
    }
178

            
179
2
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
180
2
        Some(FileDirectiveType::MetadataPdu)
181
2
    }
182
}
183

            
184
impl WritablePduPacket for MetadataPduCreator<'_, '_, '_> {
185
22
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
186
22
        let expected_len = self.len_written();
187
22
        if buf.len() < expected_len {
188
            return Err(ByteConversionError::ToSliceTooSmall {
189
                found: buf.len(),
190
                expected: expected_len,
191
            }
192
            .into());
193
22
        }
194

            
195
22
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
196
22
        buf[current_idx] = FileDirectiveType::MetadataPdu as u8;
197
22
        current_idx += 1;
198
22
        buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6)
199
22
            | (self.metadata_params.checksum_type as u8);
200
22
        current_idx += 1;
201
22
        current_idx += write_fss_field(
202
22
            self.pdu_header.common_pdu_conf().file_flag,
203
22
            self.metadata_params.file_size,
204
22
            &mut buf[current_idx..],
205
22
        )?;
206
22
        current_idx += self
207
22
            .src_file_name
208
22
            .write_to_be_bytes(&mut buf[current_idx..])?;
209
22
        current_idx += self
210
22
            .dest_file_name
211
22
            .write_to_be_bytes(&mut buf[current_idx..])?;
212
22
        buf[current_idx..current_idx + self.options.len()].copy_from_slice(self.options);
213
22
        current_idx += self.options.len();
214
22
        if self.crc_flag() == CrcFlag::WithCrc {
215
2
            current_idx = add_pdu_crc(buf, current_idx);
216
20
        }
217
22
        Ok(current_idx)
218
22
    }
219

            
220
32
    fn len_written(&self) -> usize {
221
32
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
222
32
    }
223
}
224

            
225
/// Helper structure to loop through all options of a metadata PDU. It should be noted that
226
/// iterators in Rust are not fallible, but the TLV creation can fail, for example if the raw TLV
227
/// data is invalid for some reason. In that case, the iterator will yield [None] because there
228
/// is no way to recover from this.
229
///
230
/// The user can accumulate the length of all TLVs yielded by the iterator and compare it against
231
/// the full length of the options to check whether the iterator was able to parse all TLVs
232
/// successfully.
233
pub struct OptionsIter<'opts> {
234
    opt_buf: &'opts [u8],
235
    current_idx: usize,
236
}
237

            
238
impl<'opts> Iterator for OptionsIter<'opts> {
239
    type Item = Tlv<'opts>;
240

            
241
38
    fn next(&mut self) -> Option<Self::Item> {
242
38
        if self.current_idx == self.opt_buf.len() {
243
14
            return None;
244
24
        }
245
24
        let tlv = Tlv::from_bytes(&self.opt_buf[self.current_idx..]);
246
24
        // There are not really fallible iterators so we can't continue here..
247
24
        if tlv.is_err() {
248
            return None;
249
24
        }
250
24
        let tlv = tlv.unwrap();
251
24
        self.current_idx += tlv.len_full();
252
24
        Some(tlv)
253
38
    }
254
}
255

            
256
/// Metadata PDU reader abstraction.
257
///
258
/// This abstraction exposes a specialized API for reading a metadata PDU with minimal copying
259
/// involved.
260
#[derive(Debug)]
261
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
262
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
263
pub struct MetadataPduReader<'buf> {
264
    pdu_header: PduHeader,
265
    metadata_params: MetadataGenericParams,
266
    #[cfg_attr(feature = "serde", serde(borrow))]
267
    src_file_name: Lv<'buf>,
268
    #[cfg_attr(feature = "serde", serde(borrow))]
269
    dest_file_name: Lv<'buf>,
270
    options: &'buf [u8],
271
}
272

            
273
impl<'raw> MetadataPduReader<'raw> {
274
4
    pub fn new(buf: &'raw [u8]) -> Result<Self, PduError> {
275
4
        Self::from_bytes(buf)
276
4
    }
277

            
278
14
    pub fn from_bytes(buf: &'raw [u8]) -> Result<Self, PduError> {
279
14
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
280
14
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
281
14
        let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
282
14
        // Minimal length: 1 byte + FSS (4 byte) + 2 empty LV (1 byte)
283
14
        let mut min_expected_len = current_idx + 7;
284
14
        if is_large_file {
285
6
            min_expected_len += 4;
286
8
        }
287
14
        generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
288
15
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
289
2
            PduError::InvalidDirectiveType {
290
2
                found: buf[current_idx],
291
2
                expected: Some(FileDirectiveType::MetadataPdu),
292
2
            }
293
15
        })?;
294
12
        if directive_type != FileDirectiveType::MetadataPdu {
295
2
            return Err(PduError::WrongDirectiveType {
296
2
                found: directive_type,
297
2
                expected: FileDirectiveType::MetadataPdu,
298
2
            });
299
10
        }
300
10
        current_idx += 1;
301
10
        let (fss_len, file_size) =
302
10
            read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx + 1..]);
303
10
        let metadata_params = MetadataGenericParams {
304
10
            closure_requested: ((buf[current_idx] >> 6) & 0b1) != 0,
305
10
            checksum_type: ChecksumType::try_from(buf[current_idx] & 0b1111)
306
10
                .map_err(|_| PduError::InvalidChecksumType(buf[current_idx] & 0b1111))?,
307
10
            file_size,
308
10
        };
309
10
        current_idx += 1 + fss_len;
310
10
        let src_file_name = Lv::from_bytes(&buf[current_idx..])?;
311
10
        current_idx += src_file_name.len_full();
312
10
        let dest_file_name = Lv::from_bytes(&buf[current_idx..])?;
313
10
        current_idx += dest_file_name.len_full();
314
10
        // All left-over bytes are options.
315
10
        Ok(Self {
316
10
            pdu_header,
317
10
            metadata_params,
318
10
            src_file_name,
319
10
            dest_file_name,
320
10
            options: &buf[current_idx..full_len_without_crc],
321
10
        })
322
14
    }
323

            
324
    /// Yield an iterator which can be used to loop through all options. Returns [None] if the
325
    /// options field is empty.
326
14
    pub fn options_iter(&self) -> Option<OptionsIter<'_>> {
327
14
        Some(OptionsIter {
328
14
            opt_buf: self.options,
329
14
            current_idx: 0,
330
14
        })
331
14
    }
332

            
333
4
    pub fn options(&self) -> &'raw [u8] {
334
4
        self.options
335
4
    }
336

            
337
10
    pub fn metadata_params(&self) -> &MetadataGenericParams {
338
10
        &self.metadata_params
339
10
    }
340

            
341
10
    pub fn src_file_name(&self) -> Lv {
342
10
        self.src_file_name
343
10
    }
344

            
345
10
    pub fn dest_file_name(&self) -> Lv {
346
10
        self.dest_file_name
347
10
    }
348
}
349

            
350
impl CfdpPdu for MetadataPduReader<'_> {
351
    fn pdu_header(&self) -> &PduHeader {
352
        &self.pdu_header
353
    }
354

            
355
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
356
        Some(FileDirectiveType::MetadataPdu)
357
    }
358
}
359

            
360
#[cfg(test)]
361
pub mod tests {
362
    use alloc::string::ToString;
363

            
364
    use crate::cfdp::lv::Lv;
365
    use crate::cfdp::pdu::metadata::{
366
        build_metadata_opts_from_slice, build_metadata_opts_from_vec, MetadataGenericParams,
367
        MetadataPduCreator, MetadataPduReader,
368
    };
369
    use crate::cfdp::pdu::tests::{
370
        common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID,
371
    };
372
    use crate::cfdp::pdu::{CfdpPdu, PduError, WritablePduPacket};
373
    use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
374
    use crate::cfdp::tlv::{ReadableTlv, Tlv, TlvOwned, TlvType, WritableTlv};
375
    use crate::cfdp::{
376
        ChecksumType, CrcFlag, Direction, LargeFileFlag, PduType, SegmentMetadataFlag,
377
        SegmentationControl, TransmissionMode,
378
    };
379
    use std::vec;
380

            
381
    const SRC_FILENAME: &str = "hello-world.txt";
382
    const DEST_FILENAME: &str = "hello-world2.txt";
383

            
384
22
    fn generic_metadata_pdu(
385
22
        crc_flag: CrcFlag,
386
22
        checksum_type: ChecksumType,
387
22
        closure_requested: bool,
388
22
        fss: LargeFileFlag,
389
22
        opts: &[u8],
390
22
    ) -> (
391
22
        Lv<'static>,
392
22
        Lv<'static>,
393
22
        MetadataPduCreator<'static, 'static, '_>,
394
22
    ) {
395
22
        let pdu_header = PduHeader::new_no_file_data(common_pdu_conf(crc_flag, fss), 0);
396
22
        let metadata_params = MetadataGenericParams::new(closure_requested, checksum_type, 0x1010);
397
22
        let src_filename = Lv::new_from_str(SRC_FILENAME).expect("Generating string LV failed");
398
22
        let dest_filename =
399
22
            Lv::new_from_str(DEST_FILENAME).expect("Generating destination LV failed");
400
22
        (
401
22
            src_filename,
402
22
            dest_filename,
403
22
            MetadataPduCreator::new(
404
22
                pdu_header,
405
22
                metadata_params,
406
22
                src_filename,
407
22
                dest_filename,
408
22
                opts,
409
22
            ),
410
22
        )
411
22
    }
412

            
413
    #[test]
414
2
    fn test_basic() {
415
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
416
2
            CrcFlag::NoCrc,
417
2
            ChecksumType::Crc32,
418
2
            false,
419
2
            LargeFileFlag::Normal,
420
2
            &[],
421
2
        );
422
2
        assert_eq!(
423
2
            metadata_pdu.len_written(),
424
2
            metadata_pdu.pdu_header().header_len()
425
2
                + 1
426
2
                + 1
427
2
                + 4
428
2
                + src_filename.len_full()
429
2
                + dest_filename.len_full()
430
2
        );
431
2
        assert_eq!(metadata_pdu.src_file_name(), src_filename);
432
2
        assert_eq!(metadata_pdu.dest_file_name(), dest_filename);
433
2
        assert!(metadata_pdu.options().is_empty());
434
2
        assert_eq!(metadata_pdu.crc_flag(), CrcFlag::NoCrc);
435
2
        assert_eq!(metadata_pdu.file_flag(), LargeFileFlag::Normal);
436
2
        assert_eq!(metadata_pdu.pdu_type(), PduType::FileDirective);
437
2
        assert!(!metadata_pdu.metadata_params().closure_requested);
438
2
        assert_eq!(
439
2
            metadata_pdu.metadata_params().checksum_type,
440
2
            ChecksumType::Crc32
441
2
        );
442
2
        assert_eq!(
443
2
            metadata_pdu.file_directive_type(),
444
2
            Some(FileDirectiveType::MetadataPdu)
445
2
        );
446
2
        assert_eq!(
447
2
            metadata_pdu.transmission_mode(),
448
2
            TransmissionMode::Acknowledged
449
2
        );
450
2
        assert_eq!(metadata_pdu.direction(), Direction::TowardsReceiver);
451
2
        assert_eq!(metadata_pdu.source_id(), TEST_SRC_ID.into());
452
2
        assert_eq!(metadata_pdu.dest_id(), TEST_DEST_ID.into());
453
2
        assert_eq!(metadata_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
454
2
    }
455

            
456
4
    fn check_metadata_raw_fields(
457
4
        metadata_pdu: &MetadataPduCreator,
458
4
        buf: &[u8],
459
4
        written_bytes: usize,
460
4
        checksum_type: ChecksumType,
461
4
        closure_requested: bool,
462
4
        expected_src_filename: &Lv,
463
4
        expected_dest_filename: &Lv,
464
4
    ) {
465
4
        verify_raw_header(metadata_pdu.pdu_header(), buf);
466
4
        assert_eq!(
467
4
            written_bytes,
468
4
            metadata_pdu.pdu_header.header_len()
469
4
                + 1
470
4
                + 1
471
4
                + 4
472
4
                + expected_src_filename.len_full()
473
4
                + expected_dest_filename.len_full()
474
4
        );
475
4
        assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8);
476
4
        assert_eq!(buf[8] >> 6, closure_requested as u8);
477
4
        assert_eq!(buf[8] & 0b1111, checksum_type as u8);
478
4
        assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010);
479
4
        let mut current_idx = 13;
480
4
        let src_name_from_raw =
481
4
            Lv::from_bytes(&buf[current_idx..]).expect("Creating source name LV failed");
482
4
        assert_eq!(src_name_from_raw, *expected_src_filename);
483
4
        current_idx += src_name_from_raw.len_full();
484
4
        let dest_name_from_raw =
485
4
            Lv::from_bytes(&buf[current_idx..]).expect("Creating dest name LV failed");
486
4
        assert_eq!(dest_name_from_raw, *expected_dest_filename);
487
4
        current_idx += dest_name_from_raw.len_full();
488
4
        // No options, so no additional data here.
489
4
        assert_eq!(current_idx, written_bytes);
490
4
    }
491

            
492
    #[test]
493
2
    fn test_serialization_0() {
494
2
        let checksum_type = ChecksumType::Crc32;
495
2
        let closure_requested = false;
496
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
497
2
            CrcFlag::NoCrc,
498
2
            checksum_type,
499
2
            closure_requested,
500
2
            LargeFileFlag::Normal,
501
2
            &[],
502
2
        );
503
2
        let mut buf: [u8; 64] = [0; 64];
504
2
        let res = metadata_pdu.write_to_bytes(&mut buf);
505
2
        assert!(res.is_ok());
506
2
        let written = res.unwrap();
507
2
        check_metadata_raw_fields(
508
2
            &metadata_pdu,
509
2
            &buf,
510
2
            written,
511
2
            checksum_type,
512
2
            closure_requested,
513
2
            &src_filename,
514
2
            &dest_filename,
515
2
        );
516
2
    }
517

            
518
    #[test]
519
2
    fn test_serialization_1() {
520
2
        let checksum_type = ChecksumType::Modular;
521
2
        let closure_requested = true;
522
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
523
2
            CrcFlag::NoCrc,
524
2
            checksum_type,
525
2
            closure_requested,
526
2
            LargeFileFlag::Normal,
527
2
            &[],
528
2
        );
529
2
        let mut buf: [u8; 64] = [0; 64];
530
2
        let res = metadata_pdu.write_to_bytes(&mut buf);
531
2
        assert!(res.is_ok());
532
2
        let written = res.unwrap();
533
2
        check_metadata_raw_fields(
534
2
            &metadata_pdu,
535
2
            &buf,
536
2
            written,
537
2
            checksum_type,
538
2
            closure_requested,
539
2
            &src_filename,
540
2
            &dest_filename,
541
2
        );
542
2
    }
543

            
544
    #[test]
545
2
    fn test_write_to_vec() {
546
2
        let (_, _, metadata_pdu) = generic_metadata_pdu(
547
2
            CrcFlag::NoCrc,
548
2
            ChecksumType::Crc32,
549
2
            false,
550
2
            LargeFileFlag::Normal,
551
2
            &[],
552
2
        );
553
2
        let mut buf: [u8; 64] = [0; 64];
554
2
        let pdu_vec = metadata_pdu.to_vec().unwrap();
555
2
        let written = metadata_pdu.write_to_bytes(&mut buf).unwrap();
556
2
        assert_eq!(buf[0..written], pdu_vec);
557
2
    }
558

            
559
10
    fn compare_read_pdu_to_written_pdu(written: &MetadataPduCreator, read: &MetadataPduReader) {
560
10
        assert_eq!(written.metadata_params(), read.metadata_params());
561
10
        assert_eq!(written.src_file_name(), read.src_file_name());
562
10
        assert_eq!(written.dest_file_name(), read.dest_file_name());
563
10
        let opts = written.options_iter();
564
10
        for (tlv_written, tlv_read) in opts.zip(read.options_iter().unwrap()) {
565
8
            assert_eq!(&tlv_written, &tlv_read);
566
        }
567
10
    }
568

            
569
    #[test]
570
2
    fn test_deserialization() {
571
2
        let (_, _, metadata_pdu) = generic_metadata_pdu(
572
2
            CrcFlag::NoCrc,
573
2
            ChecksumType::Crc32,
574
2
            true,
575
2
            LargeFileFlag::Normal,
576
2
            &[],
577
2
        );
578
2
        let mut buf: [u8; 64] = [0; 64];
579
2
        metadata_pdu.write_to_bytes(&mut buf).unwrap();
580
2
        let pdu_read_back = MetadataPduReader::from_bytes(&buf);
581
2
        assert!(pdu_read_back.is_ok());
582
2
        let pdu_read_back = pdu_read_back.unwrap();
583
2
        compare_read_pdu_to_written_pdu(&metadata_pdu, &pdu_read_back);
584
2
    }
585

            
586
    #[test]
587
2
    fn test_with_crc_flag() {
588
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
589
2
            CrcFlag::WithCrc,
590
2
            ChecksumType::Crc32,
591
2
            true,
592
2
            LargeFileFlag::Normal,
593
2
            &[],
594
2
        );
595
2
        assert_eq!(metadata_pdu.crc_flag(), CrcFlag::WithCrc);
596
2
        let mut buf: [u8; 64] = [0; 64];
597
2
        let write_res = metadata_pdu.write_to_bytes(&mut buf);
598
2
        assert!(write_res.is_ok());
599
2
        let written = write_res.unwrap();
600
2
        assert_eq!(
601
2
            written,
602
2
            metadata_pdu.pdu_header().header_len()
603
2
                + 1
604
2
                + 1
605
2
                + core::mem::size_of::<u32>()
606
2
                + src_filename.len_full()
607
2
                + dest_filename.len_full()
608
2
                + 2
609
2
        );
610
2
        assert_eq!(written, metadata_pdu.len_written());
611
2
        let pdu_read_back = MetadataPduReader::new(&buf).unwrap();
612
2
        compare_read_pdu_to_written_pdu(&metadata_pdu, &pdu_read_back);
613
2
    }
614

            
615
    #[test]
616
2
    fn test_with_large_file_flag() {
617
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
618
2
            CrcFlag::NoCrc,
619
2
            ChecksumType::Crc32,
620
2
            false,
621
2
            LargeFileFlag::Large,
622
2
            &[],
623
2
        );
624
2
        let mut buf: [u8; 64] = [0; 64];
625
2
        let write_res = metadata_pdu.write_to_bytes(&mut buf);
626
2
        assert!(write_res.is_ok());
627
2
        let written = write_res.unwrap();
628
2
        assert_eq!(
629
2
            written,
630
2
            metadata_pdu.pdu_header().header_len()
631
2
                + 1
632
2
                + 1
633
2
                + core::mem::size_of::<u64>()
634
2
                + src_filename.len_full()
635
2
                + dest_filename.len_full()
636
2
        );
637
2
        let pdu_read_back = MetadataPduReader::new(&buf).unwrap();
638
2
        compare_read_pdu_to_written_pdu(&metadata_pdu, &pdu_read_back);
639
2
    }
640

            
641
    #[test]
642
2
    fn test_opts_builders() {
643
2
        let tlv1 = Tlv::new_empty(TlvType::FlowLabel);
644
2
        let msg_to_user: [u8; 4] = [1, 2, 3, 4];
645
2
        let tlv2 = Tlv::new(TlvType::MsgToUser, &msg_to_user).unwrap();
646
2
        let tlv_slice = [tlv1, tlv2];
647
2
        let mut buf: [u8; 32] = [0; 32];
648
2
        let opts = build_metadata_opts_from_slice(&mut buf, &tlv_slice);
649
2
        assert!(opts.is_ok());
650
2
        let opts_len = opts.unwrap();
651
2
        assert_eq!(opts_len, tlv1.len_full() + tlv2.len_full());
652
2
        let tlv1_conv_back = Tlv::from_bytes(&buf).unwrap();
653
2
        assert_eq!(tlv1_conv_back, tlv1);
654
2
        let tlv2_conv_back = Tlv::from_bytes(&buf[tlv1_conv_back.len_full()..]).unwrap();
655
2
        assert_eq!(tlv2_conv_back, tlv2);
656
2
    }
657

            
658
    #[test]
659
2
    fn test_opts_builders_from_vec() {
660
2
        let tlv1 = Tlv::new_empty(TlvType::FlowLabel);
661
2
        let msg_to_user: [u8; 4] = [1, 2, 3, 4];
662
2
        let tlv2 = Tlv::new(TlvType::MsgToUser, &msg_to_user).unwrap();
663
2
        let tlv_vec = vec![tlv1, tlv2];
664
2
        let mut buf: [u8; 32] = [0; 32];
665
2
        let opts = build_metadata_opts_from_vec(&mut buf, &tlv_vec);
666
2
        assert!(opts.is_ok());
667
2
        let opts_len = opts.unwrap();
668
2
        assert_eq!(opts_len, tlv1.len_full() + tlv2.len_full());
669
2
        let tlv1_conv_back = Tlv::from_bytes(&buf).unwrap();
670
2
        assert_eq!(tlv1_conv_back, tlv1);
671
2
        let tlv2_conv_back = Tlv::from_bytes(&buf[tlv1_conv_back.len_full()..]).unwrap();
672
2
        assert_eq!(tlv2_conv_back, tlv2);
673
2
    }
674

            
675
    #[test]
676
2
    fn test_with_opts() {
677
2
        let tlv1 = Tlv::new_empty(TlvType::FlowLabel);
678
2
        let msg_to_user: [u8; 4] = [1, 2, 3, 4];
679
2
        let tlv2 = Tlv::new(TlvType::MsgToUser, &msg_to_user).unwrap();
680
2
        let mut tlv_buf: [u8; 64] = [0; 64];
681
2
        let opts_len = build_metadata_opts_from_slice(&mut tlv_buf, &[tlv1, tlv2]).unwrap();
682
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
683
2
            CrcFlag::NoCrc,
684
2
            ChecksumType::Crc32,
685
2
            false,
686
2
            LargeFileFlag::Normal,
687
2
            &tlv_buf[0..opts_len],
688
2
        );
689
2
        let mut buf: [u8; 128] = [0; 128];
690
2
        let write_res = metadata_pdu.write_to_bytes(&mut buf);
691
2
        assert!(write_res.is_ok());
692
2
        let written = write_res.unwrap();
693
2
        assert_eq!(
694
2
            written,
695
2
            metadata_pdu.pdu_header.header_len()
696
2
                + 1
697
2
                + 1
698
2
                + 4
699
2
                + src_filename.len_full()
700
2
                + dest_filename.len_full()
701
2
                + opts_len
702
2
        );
703
2
        let pdu_read_back = MetadataPduReader::from_bytes(&buf).unwrap();
704
2
        compare_read_pdu_to_written_pdu(&metadata_pdu, &pdu_read_back);
705
2
        let opts_iter = pdu_read_back.options_iter();
706
2
        assert!(opts_iter.is_some());
707
2
        let opts_iter = opts_iter.unwrap();
708
2
        let mut accumulated_len = 0;
709
4
        for (idx, opt) in opts_iter.enumerate() {
710
4
            if idx == 0 {
711
2
                assert_eq!(tlv1, opt);
712
2
            } else if idx == 1 {
713
2
                assert_eq!(tlv2, opt);
714
            }
715
4
            accumulated_len += opt.len_full();
716
        }
717
2
        assert_eq!(accumulated_len, pdu_read_back.options().len());
718
2
    }
719
    #[test]
720
2
    fn test_with_owned_opts() {
721
2
        let tlv1 = TlvOwned::new_empty(TlvType::FlowLabel);
722
2
        let msg_to_user: [u8; 4] = [1, 2, 3, 4];
723
2
        let tlv2 = TlvOwned::new(TlvType::MsgToUser, &msg_to_user).unwrap();
724
2
        let mut all_tlvs = tlv1.to_vec();
725
2
        all_tlvs.extend(tlv2.to_vec());
726
2
        let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu(
727
2
            CrcFlag::NoCrc,
728
2
            ChecksumType::Crc32,
729
2
            false,
730
2
            LargeFileFlag::Normal,
731
2
            &all_tlvs,
732
2
        );
733
2
        let mut buf: [u8; 128] = [0; 128];
734
2
        let write_res = metadata_pdu.write_to_bytes(&mut buf);
735
2
        assert!(write_res.is_ok());
736
2
        let written = write_res.unwrap();
737
2
        assert_eq!(
738
2
            written,
739
2
            metadata_pdu.pdu_header.header_len()
740
2
                + 1
741
2
                + 1
742
2
                + 4
743
2
                + src_filename.len_full()
744
2
                + dest_filename.len_full()
745
2
                + all_tlvs.len()
746
2
        );
747
2
        let pdu_read_back = MetadataPduReader::from_bytes(&buf).unwrap();
748
2
        compare_read_pdu_to_written_pdu(&metadata_pdu, &pdu_read_back);
749
2
        let opts_iter = pdu_read_back.options_iter();
750
2
        assert!(opts_iter.is_some());
751
2
        let opts_iter = opts_iter.unwrap();
752
2
        let mut accumulated_len = 0;
753
4
        for (idx, opt) in opts_iter.enumerate() {
754
4
            if idx == 0 {
755
2
                assert_eq!(tlv1, opt);
756
2
            } else if idx == 1 {
757
2
                assert_eq!(tlv2, opt);
758
            }
759
4
            accumulated_len += opt.len_full();
760
        }
761
2
        assert_eq!(accumulated_len, pdu_read_back.options().len());
762
2
    }
763

            
764
    #[test]
765
2
    fn test_invalid_directive_code() {
766
2
        let (_, _, metadata_pdu) = generic_metadata_pdu(
767
2
            CrcFlag::NoCrc,
768
2
            ChecksumType::Crc32,
769
2
            true,
770
2
            LargeFileFlag::Large,
771
2
            &[],
772
2
        );
773
2
        let mut metadata_vec = metadata_pdu.to_vec().unwrap();
774
2
        metadata_vec[7] = 0xff;
775
2
        let metadata_error = MetadataPduReader::from_bytes(&metadata_vec);
776
2
        assert!(metadata_error.is_err());
777
2
        let error = metadata_error.unwrap_err();
778
2
        if let PduError::InvalidDirectiveType { found, expected } = error {
779
2
            assert_eq!(found, 0xff);
780
2
            assert_eq!(expected, Some(FileDirectiveType::MetadataPdu));
781
2
            assert_eq!(
782
2
                error.to_string(),
783
2
                "invalid directive type value 255, expected Some(MetadataPdu)"
784
2
            );
785
        } else {
786
            panic!("Expected InvalidDirectiveType error, got {:?}", error);
787
        }
788
2
    }
789

            
790
    #[test]
791
2
    fn test_wrong_directive_code() {
792
2
        let (_, _, metadata_pdu) = generic_metadata_pdu(
793
2
            CrcFlag::NoCrc,
794
2
            ChecksumType::Crc32,
795
2
            false,
796
2
            LargeFileFlag::Large,
797
2
            &[],
798
2
        );
799
2
        let mut metadata_vec = metadata_pdu.to_vec().unwrap();
800
2
        metadata_vec[7] = FileDirectiveType::EofPdu as u8;
801
2
        let metadata_error = MetadataPduReader::from_bytes(&metadata_vec);
802
2
        assert!(metadata_error.is_err());
803
2
        let error = metadata_error.unwrap_err();
804
2
        if let PduError::WrongDirectiveType { found, expected } = error {
805
2
            assert_eq!(found, FileDirectiveType::EofPdu);
806
2
            assert_eq!(expected, FileDirectiveType::MetadataPdu);
807
2
            assert_eq!(
808
2
                error.to_string(),
809
2
                "found directive type EofPdu, expected MetadataPdu"
810
2
            );
811
        } else {
812
            panic!("Expected InvalidDirectiveType error, got {:?}", error);
813
        }
814
2
    }
815
    #[test]
816
2
    fn test_corrects_pdu_header() {
817
2
        let pdu_header = PduHeader::new_for_file_data(
818
2
            common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal),
819
2
            0,
820
2
            SegmentMetadataFlag::NotPresent,
821
2
            SegmentationControl::NoRecordBoundaryPreservation,
822
2
        );
823
2
        let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 10);
824
2
        let src_filename = Lv::new_from_str(SRC_FILENAME).expect("Generating string LV failed");
825
2
        let dest_filename =
826
2
            Lv::new_from_str(DEST_FILENAME).expect("Generating destination LV failed");
827
2
        let metadata_pdu = MetadataPduCreator::new_no_opts(
828
2
            pdu_header,
829
2
            metadata_params,
830
2
            src_filename,
831
2
            dest_filename,
832
2
        );
833
2
        assert_eq!(metadata_pdu.pdu_header().pdu_type(), PduType::FileDirective);
834
2
    }
835
}