1
use spacepackets::{
2
    cfdp::{
3
        tlv::{GenericTlv, Tlv, TlvType},
4
        SegmentationControl, TransmissionMode,
5
    },
6
    util::UnsignedByteField,
7
};
8

            
9
#[cfg(feature = "alloc")]
10
pub use alloc_mod::*;
11

            
12
#[derive(Debug, PartialEq, Eq)]
13
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15
pub struct FilePathTooLarge(pub usize);
16

            
17
/// This trait is an abstraction for different Put Request structures which can be used
18
/// by Put Request consumers.
19
pub trait ReadablePutRequest {
20
    fn destination_id(&self) -> UnsignedByteField;
21
    fn source_file(&self) -> Option<&str>;
22
    fn dest_file(&self) -> Option<&str>;
23
    fn trans_mode(&self) -> Option<TransmissionMode>;
24
    fn closure_requested(&self) -> Option<bool>;
25
    fn seg_ctrl(&self) -> Option<SegmentationControl>;
26

            
27
    fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>>;
28
    fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>>;
29
    fn flow_label(&self) -> Option<Tlv>;
30
    fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>>;
31
}
32

            
33
#[derive(Debug, PartialEq, Eq)]
34
pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> {
35
    pub destination_id: UnsignedByteField,
36
    source_file: Option<&'src_file str>,
37
    dest_file: Option<&'dest_file str>,
38
    pub trans_mode: Option<TransmissionMode>,
39
    pub closure_requested: Option<bool>,
40
    pub seg_ctrl: Option<SegmentationControl>,
41
    pub msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
42
    pub fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
43
    pub flow_label: Option<Tlv<'flow_label>>,
44
    pub fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
45
}
46

            
47
impl<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
48
    PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
49
{
50
    #[allow(clippy::too_many_arguments)]
51
2
    pub fn new(
52
2
        destination_id: UnsignedByteField,
53
2
        source_file: Option<&'src_file str>,
54
2
        dest_file: Option<&'dest_file str>,
55
2
        trans_mode: Option<TransmissionMode>,
56
2
        closure_requested: Option<bool>,
57
2
        seg_ctrl: Option<SegmentationControl>,
58
2
        msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
59
2
        fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
60
2
        flow_label: Option<Tlv<'flow_label>>,
61
2
        fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
62
2
    ) -> Result<Self, FilePathTooLarge> {
63
2
        generic_path_checks(source_file, dest_file)?;
64
2
        Ok(Self {
65
2
            destination_id,
66
2
            source_file,
67
2
            dest_file,
68
2
            trans_mode,
69
2
            closure_requested,
70
2
            seg_ctrl,
71
2
            msgs_to_user,
72
2
            fault_handler_overrides,
73
2
            flow_label,
74
2
            fs_requests,
75
2
        })
76
2
    }
77
}
78

            
79
impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> {
80
6
    fn destination_id(&self) -> UnsignedByteField {
81
6
        self.destination_id
82
6
    }
83

            
84
6
    fn source_file(&self) -> Option<&str> {
85
6
        self.source_file
86
6
    }
87

            
88
6
    fn dest_file(&self) -> Option<&str> {
89
6
        self.dest_file
90
6
    }
91

            
92
6
    fn trans_mode(&self) -> Option<TransmissionMode> {
93
6
        self.trans_mode
94
6
    }
95

            
96
6
    fn closure_requested(&self) -> Option<bool> {
97
6
        self.closure_requested
98
6
    }
99

            
100
6
    fn seg_ctrl(&self) -> Option<SegmentationControl> {
101
6
        self.seg_ctrl
102
6
    }
103

            
104
8
    fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
105
8
        if let Some(msgs_to_user) = self.msgs_to_user {
106
2
            return Some(msgs_to_user.iter().copied());
107
6
        }
108
6
        None
109
8
    }
110

            
111
2
    fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
112
2
        if let Some(fh_overrides) = self.fault_handler_overrides {
113
            return Some(fh_overrides.iter().copied());
114
2
        }
115
2
        None
116
2
    }
117

            
118
2
    fn flow_label(&self) -> Option<Tlv> {
119
2
        self.flow_label
120
2
    }
121

            
122
6
    fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
123
6
        if let Some(fs_requests) = self.msgs_to_user {
124
            return Some(fs_requests.iter().copied());
125
6
        }
126
6
        None
127
6
    }
128
}
129

            
130
16
pub fn generic_path_checks(
131
16
    source_file: Option<&str>,
132
16
    dest_file: Option<&str>,
133
16
) -> Result<(), FilePathTooLarge> {
134
16
    if let Some(src_file) = source_file {
135
16
        if src_file.len() > u8::MAX as usize {
136
2
            return Err(FilePathTooLarge(src_file.len()));
137
14
        }
138
    }
139
14
    if let Some(dest_file) = dest_file {
140
14
        if dest_file.len() > u8::MAX as usize {
141
2
            return Err(FilePathTooLarge(dest_file.len()));
142
12
        }
143
    }
144
12
    Ok(())
145
16
}
146

            
147
impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> {
148
14
    pub fn new_regular_request(
149
14
        dest_id: UnsignedByteField,
150
14
        source_file: &'src_file str,
151
14
        dest_file: &'dest_file str,
152
14
        trans_mode: Option<TransmissionMode>,
153
14
        closure_requested: Option<bool>,
154
14
    ) -> Result<Self, FilePathTooLarge> {
155
14
        generic_path_checks(Some(source_file), Some(dest_file))?;
156
10
        Ok(Self {
157
10
            destination_id: dest_id,
158
10
            source_file: Some(source_file),
159
10
            dest_file: Some(dest_file),
160
10
            trans_mode,
161
10
            closure_requested,
162
10
            seg_ctrl: None,
163
10
            msgs_to_user: None,
164
10
            fault_handler_overrides: None,
165
10
            flow_label: None,
166
10
            fs_requests: None,
167
10
        })
168
14
    }
169
}
170

            
171
#[derive(Debug, PartialEq, Eq)]
172
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174
pub struct TlvWithInvalidType(pub(crate) ());
175

            
176
impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> {
177
2
    pub fn new_msgs_to_user_only(
178
2
        dest_id: UnsignedByteField,
179
2
        msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>],
180
2
    ) -> Result<Self, TlvWithInvalidType> {
181
2
        Ok(Self {
182
2
            destination_id: dest_id,
183
2
            source_file: None,
184
2
            dest_file: None,
185
2
            trans_mode: None,
186
2
            closure_requested: None,
187
2
            seg_ctrl: None,
188
2
            msgs_to_user: Some(msgs_to_user),
189
2
            fault_handler_overrides: None,
190
2
            flow_label: None,
191
2
            fs_requests: None,
192
2
        })
193
2
    }
194

            
195
    /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
196
2
    pub fn check_tlv_type_validities(&self) -> bool {
197
2
        generic_tlv_list_type_check(self.msgs_to_user, TlvType::MsgToUser);
198
2
        if let Some(flow_label) = &self.flow_label {
199
            if flow_label.tlv_type().is_none() {
200
                return false;
201
            }
202
            if flow_label.tlv_type().unwrap() != TlvType::FlowLabel {
203
                return false;
204
            }
205
2
        }
206
2
        generic_tlv_list_type_check(self.fault_handler_overrides, TlvType::FaultHandler);
207
2
        generic_tlv_list_type_check(self.fs_requests, TlvType::FilestoreRequest);
208
2
        true
209
2
    }
210
}
211

            
212
12
pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
213
12
    opt_tlvs: Option<&[TlvProvider]>,
214
12
    tlv_type: TlvType,
215
12
) -> bool {
216
12
    if let Some(tlvs) = opt_tlvs {
217
8
        for tlv in tlvs {
218
4
            if tlv.tlv_type().is_none() {
219
                return false;
220
4
            }
221
4
            if tlv.tlv_type().unwrap() != tlv_type {
222
                return false;
223
4
            }
224
        }
225
8
    }
226
12
    true
227
12
}
228

            
229
#[cfg(feature = "alloc")]
230
pub mod alloc_mod {
231
    use core::str::Utf8Error;
232

            
233
    use super::*;
234
    use alloc::string::ToString;
235
    use spacepackets::{
236
        cfdp::tlv::{msg_to_user::MsgToUserTlv, ReadableTlv, TlvOwned, WritableTlv},
237
        ByteConversionError,
238
    };
239

            
240
    /// Owned variant of [PutRequest] with no lifetimes which is also [Clone]able.
241
    #[derive(Debug, Clone, PartialEq, Eq)]
242
    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
243
    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
244
    pub struct PutRequestOwned {
245
        pub destination_id: UnsignedByteField,
246
        source_file: Option<alloc::string::String>,
247
        dest_file: Option<alloc::string::String>,
248
        pub trans_mode: Option<TransmissionMode>,
249
        pub closure_requested: Option<bool>,
250
        pub seg_ctrl: Option<SegmentationControl>,
251
        pub msgs_to_user: Option<alloc::vec::Vec<TlvOwned>>,
252
        pub fault_handler_overrides: Option<alloc::vec::Vec<TlvOwned>>,
253
        pub flow_label: Option<TlvOwned>,
254
        pub fs_requests: Option<alloc::vec::Vec<TlvOwned>>,
255
    }
256

            
257
    impl PutRequestOwned {
258
36
        pub fn new_regular_request(
259
36
            dest_id: UnsignedByteField,
260
36
            source_file: &str,
261
36
            dest_file: &str,
262
36
            trans_mode: Option<TransmissionMode>,
263
36
            closure_requested: Option<bool>,
264
36
        ) -> Result<Self, FilePathTooLarge> {
265
36
            if source_file.len() > u8::MAX as usize {
266
2
                return Err(FilePathTooLarge(source_file.len()));
267
34
            }
268
34
            if dest_file.len() > u8::MAX as usize {
269
2
                return Err(FilePathTooLarge(dest_file.len()));
270
32
            }
271
32
            Ok(Self {
272
32
                destination_id: dest_id,
273
32
                source_file: Some(source_file.to_string()),
274
32
                dest_file: Some(dest_file.to_string()),
275
32
                trans_mode,
276
32
                closure_requested,
277
32
                seg_ctrl: None,
278
32
                msgs_to_user: None,
279
32
                fault_handler_overrides: None,
280
32
                flow_label: None,
281
32
                fs_requests: None,
282
32
            })
283
36
        }
284

            
285
2
        pub fn new_msgs_to_user_only(
286
2
            dest_id: UnsignedByteField,
287
2
            msgs_to_user: &[MsgToUserTlv<'_>],
288
2
        ) -> Result<Self, TlvWithInvalidType> {
289
2
            Ok(Self {
290
2
                destination_id: dest_id,
291
2
                source_file: None,
292
2
                dest_file: None,
293
2
                trans_mode: None,
294
2
                closure_requested: None,
295
2
                seg_ctrl: None,
296
3
                msgs_to_user: Some(msgs_to_user.iter().map(|msg| msg.tlv.to_owned()).collect()),
297
2
                fault_handler_overrides: None,
298
2
                flow_label: None,
299
2
                fs_requests: None,
300
2
            })
301
2
        }
302

            
303
        /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
304
2
        pub fn check_tlv_type_validities(&self) -> bool {
305
2
            generic_tlv_list_type_check(self.msgs_to_user.as_deref(), TlvType::MsgToUser);
306
2
            if let Some(flow_label) = &self.flow_label {
307
                if flow_label.tlv_type().is_none() {
308
                    return false;
309
                }
310
                if flow_label.tlv_type().unwrap() != TlvType::FlowLabel {
311
                    return false;
312
                }
313
2
            }
314
2
            generic_tlv_list_type_check(
315
2
                self.fault_handler_overrides.as_deref(),
316
2
                TlvType::FaultHandler,
317
2
            );
318
2
            generic_tlv_list_type_check(self.fs_requests.as_deref(), TlvType::FilestoreRequest);
319
2
            true
320
2
        }
321
    }
322

            
323
    impl From<PutRequest<'_, '_, '_, '_, '_, '_>> for PutRequestOwned {
324
2
        fn from(req: PutRequest<'_, '_, '_, '_, '_, '_>) -> Self {
325
2
            Self {
326
2
                destination_id: req.destination_id,
327
3
                source_file: req.source_file.map(|s| s.into()),
328
3
                dest_file: req.dest_file.map(|s| s.into()),
329
2
                trans_mode: req.trans_mode,
330
2
                closure_requested: req.closure_requested,
331
2
                seg_ctrl: req.seg_ctrl,
332
2
                msgs_to_user: req
333
2
                    .msgs_to_user
334
2
                    .map(|msgs_to_user| msgs_to_user.iter().map(|msg| msg.to_owned()).collect()),
335
2
                fault_handler_overrides: req
336
2
                    .msgs_to_user
337
2
                    .map(|fh_overides| fh_overides.iter().map(|msg| msg.to_owned()).collect()),
338
2
                flow_label: req
339
2
                    .flow_label
340
2
                    .map(|flow_label_tlv| flow_label_tlv.to_owned()),
341
2
                fs_requests: req
342
2
                    .fs_requests
343
2
                    .map(|fs_requests| fs_requests.iter().map(|msg| msg.to_owned()).collect()),
344
2
            }
345
2
        }
346
    }
347

            
348
    impl ReadablePutRequest for PutRequestOwned {
349
34
        fn destination_id(&self) -> UnsignedByteField {
350
34
            self.destination_id
351
34
        }
352

            
353
34
        fn source_file(&self) -> Option<&str> {
354
34
            self.source_file.as_deref()
355
34
        }
356

            
357
34
        fn dest_file(&self) -> Option<&str> {
358
34
            self.dest_file.as_deref()
359
34
        }
360

            
361
34
        fn trans_mode(&self) -> Option<TransmissionMode> {
362
34
            self.trans_mode
363
34
        }
364

            
365
34
        fn closure_requested(&self) -> Option<bool> {
366
34
            self.closure_requested
367
34
        }
368

            
369
32
        fn seg_ctrl(&self) -> Option<SegmentationControl> {
370
32
            self.seg_ctrl
371
32
        }
372

            
373
36
        fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
374
36
            if let Some(msgs_to_user) = &self.msgs_to_user {
375
3
                return Some(msgs_to_user.iter().map(|tlv_owned| tlv_owned.as_tlv()));
376
34
            }
377
34
            None
378
36
        }
379

            
380
2
        fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
381
2
            if let Some(fh_overrides) = &self.fault_handler_overrides {
382
                return Some(fh_overrides.iter().map(|tlv_owned| tlv_owned.as_tlv()));
383
2
            }
384
2
            None
385
2
        }
386

            
387
4
        fn flow_label(&self) -> Option<Tlv> {
388
4
            self.flow_label.as_ref().map(|tlv| tlv.as_tlv())
389
4
        }
390

            
391
32
        fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
392
32
            if let Some(requests) = &self.fs_requests {
393
                return Some(requests.iter().map(|tlv_owned| tlv_owned.as_tlv()));
394
32
            }
395
32
            None
396
32
        }
397
    }
398

            
399
    pub struct StaticPutRequestFields {
400
        pub destination_id: UnsignedByteField,
401
        /// Static buffer to store source file path.
402
        pub source_file_buf: [u8; u8::MAX as usize],
403
        /// Current source path length.
404
        pub source_file_len: usize,
405
        /// Static buffer to store dest file path.
406
        pub dest_file_buf: [u8; u8::MAX as usize],
407
        /// Current destination path length.
408
        pub dest_file_len: usize,
409
        pub trans_mode: Option<TransmissionMode>,
410
        pub closure_requested: Option<bool>,
411
        pub seg_ctrl: Option<SegmentationControl>,
412
    }
413

            
414
    impl Default for StaticPutRequestFields {
415
38
        fn default() -> Self {
416
38
            Self {
417
38
                destination_id: UnsignedByteField::new(0, 0),
418
38
                source_file_buf: [0; u8::MAX as usize],
419
38
                source_file_len: Default::default(),
420
38
                dest_file_buf: [0; u8::MAX as usize],
421
38
                dest_file_len: Default::default(),
422
38
                trans_mode: Default::default(),
423
38
                closure_requested: Default::default(),
424
38
                seg_ctrl: Default::default(),
425
38
            }
426
38
        }
427
    }
428

            
429
    impl StaticPutRequestFields {
430
2
        pub fn clear(&mut self) {
431
2
            self.destination_id = UnsignedByteField::new(0, 0);
432
2
            self.source_file_len = 0;
433
2
            self.dest_file_len = 0;
434
2
            self.trans_mode = None;
435
2
            self.closure_requested = None;
436
2
            self.seg_ctrl = None;
437
2
        }
438
    }
439

            
440
    /// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
441
    /// without requiring run-time allocation. The user must specify the static buffer sizes used
442
    /// to store TLVs or list of TLVs.
443
    pub struct StaticPutRequestCacher {
444
        pub static_fields: StaticPutRequestFields,
445
        opts_buf: alloc::vec::Vec<u8>,
446
        opts_len: usize, // fs_request_start_end_pos: Option<(usize, usize)>
447
    }
448

            
449
    impl StaticPutRequestCacher {
450
38
        pub fn new(max_len_opts_buf: usize) -> Self {
451
38
            Self {
452
38
                static_fields: StaticPutRequestFields::default(),
453
38
                opts_buf: alloc::vec![0; max_len_opts_buf],
454
38
                opts_len: 0,
455
38
            }
456
38
        }
457

            
458
30
        pub fn set(
459
30
            &mut self,
460
30
            put_request: &impl ReadablePutRequest,
461
30
        ) -> Result<(), ByteConversionError> {
462
30
            self.static_fields.destination_id = put_request.destination_id();
463
30
            if let Some(source_file) = put_request.source_file() {
464
30
                if source_file.len() > u8::MAX as usize {
465
                    return Err(ByteConversionError::ToSliceTooSmall {
466
                        found: self.static_fields.source_file_buf.len(),
467
                        expected: source_file.len(),
468
                    });
469
30
                }
470
30
                self.static_fields.source_file_buf[..source_file.len()]
471
30
                    .copy_from_slice(source_file.as_bytes());
472
30
                self.static_fields.source_file_len = source_file.len();
473
            }
474
30
            if let Some(dest_file) = put_request.dest_file() {
475
30
                if dest_file.len() > u8::MAX as usize {
476
                    return Err(ByteConversionError::ToSliceTooSmall {
477
                        found: self.static_fields.source_file_buf.len(),
478
                        expected: dest_file.len(),
479
                    });
480
30
                }
481
30
                self.static_fields.dest_file_buf[..dest_file.len()]
482
30
                    .copy_from_slice(dest_file.as_bytes());
483
30
                self.static_fields.dest_file_len = dest_file.len();
484
            }
485
30
            self.static_fields.trans_mode = put_request.trans_mode();
486
30
            self.static_fields.closure_requested = put_request.closure_requested();
487
30
            self.static_fields.seg_ctrl = put_request.seg_ctrl();
488
30
            let mut current_idx = 0;
489
30
            let mut store_tlv = |tlv: &Tlv| {
490
                if current_idx + tlv.len_full() > self.opts_buf.len() {
491
                    return Err(ByteConversionError::ToSliceTooSmall {
492
                        found: self.opts_buf.len(),
493
                        expected: current_idx + tlv.len_full(),
494
                    });
495
                }
496
                // We checked the buffer lengths, so this should never fail.
497
                tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
498
                    .unwrap();
499
                current_idx += tlv.len_full();
500
                Ok(())
501
            };
502
30
            if let Some(fs_req) = put_request.fs_requests() {
503
                for fs_req in fs_req {
504
                    store_tlv(&fs_req)?;
505
                }
506
30
            }
507
30
            if let Some(msgs_to_user) = put_request.msgs_to_user() {
508
                for msg_to_user in msgs_to_user {
509
                    store_tlv(&msg_to_user)?;
510
                }
511
30
            }
512
30
            self.opts_len = current_idx;
513
30
            Ok(())
514
30
        }
515

            
516
52
        pub fn has_source_file(&self) -> bool {
517
52
            self.static_fields.source_file_len > 0
518
52
        }
519

            
520
        pub fn has_dest_file(&self) -> bool {
521
            self.static_fields.dest_file_len > 0
522
        }
523

            
524
128
        pub fn source_file(&self) -> Result<&str, Utf8Error> {
525
128
            core::str::from_utf8(
526
128
                &self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
527
128
            )
528
128
        }
529

            
530
50
        pub fn dest_file(&self) -> Result<&str, Utf8Error> {
531
50
            core::str::from_utf8(
532
50
                &self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len],
533
50
            )
534
50
        }
535

            
536
6
        pub fn opts_len(&self) -> usize {
537
6
            self.opts_len
538
6
        }
539

            
540
26
        pub fn opts_slice(&self) -> &[u8] {
541
26
            &self.opts_buf[0..self.opts_len]
542
26
        }
543

            
544
        /// This clears the cacher structure. This is a cheap operation because it only
545
        /// sets [Option]al values to [None] and the length of stores TLVs to 0.
546
        ///
547
        /// Please note that this method will not set the values in the buffer to 0.
548
2
        pub fn clear(&mut self) {
549
2
            self.static_fields.clear();
550
2
            self.opts_len = 0;
551
2
        }
552
    }
553
}
554

            
555
#[cfg(test)]
556
mod tests {
557
    use std::string::String;
558

            
559
    use spacepackets::{
560
        cfdp::tlv::{msg_to_user::MsgToUserTlv, ReadableTlv},
561
        util::UbfU16,
562
    };
563

            
564
    use super::*;
565

            
566
    pub const DEST_ID: UbfU16 = UbfU16::new(5);
567

            
568
    #[test]
569
2
    fn test_put_request_basic() {
570
2
        let src_file = "/tmp/hello.txt";
571
2
        let dest_file = "/tmp/hello2.txt";
572
2
        let put_request = PutRequest::new(
573
2
            DEST_ID.into(),
574
2
            Some(src_file),
575
2
            Some(dest_file),
576
2
            None,
577
2
            None,
578
2
            None,
579
2
            None,
580
2
            None,
581
2
            None,
582
2
            None,
583
2
        )
584
2
        .unwrap();
585
2
        let identical_request =
586
2
            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
587
2
                .unwrap();
588
2
        assert_eq!(put_request, identical_request);
589
2
    }
590

            
591
    #[test]
592
2
    fn test_put_request_path_checks_source_too_long() {
593
2
        let mut invalid_path = String::from("/tmp/");
594
2
        invalid_path += "a".repeat(u8::MAX as usize).as_str();
595
2
        let dest_file = "/tmp/hello2.txt";
596
2
        let error =
597
2
            PutRequest::new_regular_request(DEST_ID.into(), &invalid_path, dest_file, None, None);
598
2
        assert!(error.is_err());
599
2
        let error = error.unwrap_err();
600
2
        assert_eq!(u8::MAX as usize + 5, error.0);
601
2
    }
602

            
603
    #[test]
604
2
    fn test_put_request_path_checks_dest_file_too_long() {
605
2
        let mut invalid_path = String::from("/tmp/");
606
2
        invalid_path += "a".repeat(u8::MAX as usize).as_str();
607
2
        let source_file = "/tmp/hello2.txt";
608
2
        let error =
609
2
            PutRequest::new_regular_request(DEST_ID.into(), source_file, &invalid_path, None, None);
610
2
        assert!(error.is_err());
611
2
        let error = error.unwrap_err();
612
2
        assert_eq!(u8::MAX as usize + 5, error.0);
613
2
    }
614

            
615
    #[test]
616
2
    fn test_owned_put_request_path_checks_source_too_long() {
617
2
        let mut invalid_path = String::from("/tmp/");
618
2
        invalid_path += "a".repeat(u8::MAX as usize).as_str();
619
2
        let dest_file = "/tmp/hello2.txt";
620
2
        let error = PutRequestOwned::new_regular_request(
621
2
            DEST_ID.into(),
622
2
            &invalid_path,
623
2
            dest_file,
624
2
            None,
625
2
            None,
626
2
        );
627
2
        assert!(error.is_err());
628
2
        let error = error.unwrap_err();
629
2
        assert_eq!(u8::MAX as usize + 5, error.0);
630
2
    }
631

            
632
    #[test]
633
2
    fn test_owned_put_request_path_checks_dest_file_too_long() {
634
2
        let mut invalid_path = String::from("/tmp/");
635
2
        invalid_path += "a".repeat(u8::MAX as usize).as_str();
636
2
        let source_file = "/tmp/hello2.txt";
637
2
        let error = PutRequestOwned::new_regular_request(
638
2
            DEST_ID.into(),
639
2
            source_file,
640
2
            &invalid_path,
641
2
            None,
642
2
            None,
643
2
        );
644
2
        assert!(error.is_err());
645
2
        let error = error.unwrap_err();
646
2
        assert_eq!(u8::MAX as usize + 5, error.0);
647
2
    }
648

            
649
    #[test]
650
2
    fn test_put_request_basic_small_ctor() {
651
2
        let src_file = "/tmp/hello.txt";
652
2
        let dest_file = "/tmp/hello2.txt";
653
2
        let put_request =
654
2
            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
655
2
                .unwrap();
656
2
        assert_eq!(put_request.source_file(), Some(src_file));
657
2
        assert_eq!(put_request.dest_file(), Some(dest_file));
658
2
        assert_eq!(put_request.destination_id(), DEST_ID.into());
659
2
        assert_eq!(put_request.seg_ctrl(), None);
660
2
        assert_eq!(put_request.closure_requested(), None);
661
2
        assert_eq!(put_request.trans_mode(), None);
662
2
        assert!(put_request.fs_requests().is_none());
663
2
        assert!(put_request.msgs_to_user().is_none());
664
2
        assert!(put_request.fault_handler_overrides().is_none());
665
2
        assert!(put_request.flow_label().is_none());
666
2
    }
667

            
668
    #[test]
669
2
    fn test_put_request_owned_basic() {
670
2
        let src_file = "/tmp/hello.txt";
671
2
        let dest_file = "/tmp/hello2.txt";
672
2
        let put_request =
673
2
            PutRequestOwned::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
674
2
                .unwrap();
675
2
        assert_eq!(put_request.source_file(), Some(src_file));
676
2
        assert_eq!(put_request.dest_file(), Some(dest_file));
677
2
        assert_eq!(put_request.destination_id(), DEST_ID.into());
678
2
        assert_eq!(put_request.seg_ctrl(), None);
679
2
        assert_eq!(put_request.closure_requested(), None);
680
2
        assert_eq!(put_request.trans_mode(), None);
681
2
        assert!(put_request.flow_label().is_none());
682
2
        assert!(put_request.fs_requests().is_none());
683
2
        assert!(put_request.msgs_to_user().is_none());
684
2
        assert!(put_request.fault_handler_overrides().is_none());
685
2
        assert!(put_request.flow_label().is_none());
686
2
        let put_request_cloned = put_request.clone();
687
2
        assert_eq!(put_request, put_request_cloned);
688
2
    }
689

            
690
    #[test]
691
2
    fn test_put_request_cacher_basic() {
692
2
        let put_request_cached = StaticPutRequestCacher::new(128);
693
2
        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
694
2
        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
695
2
        assert_eq!(put_request_cached.opts_len(), 0);
696
2
        assert_eq!(put_request_cached.opts_slice(), &[]);
697
2
    }
698

            
699
    #[test]
700
2
    fn test_put_request_cacher_set() {
701
2
        let mut put_request_cached = StaticPutRequestCacher::new(128);
702
2
        let src_file = "/tmp/hello.txt";
703
2
        let dest_file = "/tmp/hello2.txt";
704
2
        let put_request =
705
2
            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
706
2
                .unwrap();
707
2
        put_request_cached.set(&put_request).unwrap();
708
2
        assert_eq!(
709
2
            put_request_cached.static_fields.source_file_len,
710
2
            src_file.len()
711
2
        );
712
2
        assert_eq!(
713
2
            put_request_cached.static_fields.dest_file_len,
714
2
            dest_file.len()
715
2
        );
716
2
        assert_eq!(put_request_cached.source_file().unwrap(), src_file);
717
2
        assert_eq!(put_request_cached.dest_file().unwrap(), dest_file);
718
2
        assert_eq!(put_request_cached.opts_len(), 0);
719
2
    }
720

            
721
    #[test]
722
2
    fn test_put_request_cacher_set_and_clear() {
723
2
        let mut put_request_cached = StaticPutRequestCacher::new(128);
724
2
        let src_file = "/tmp/hello.txt";
725
2
        let dest_file = "/tmp/hello2.txt";
726
2
        let put_request =
727
2
            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
728
2
                .unwrap();
729
2
        put_request_cached.set(&put_request).unwrap();
730
2
        put_request_cached.clear();
731
2
        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
732
2
        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
733
2
        assert_eq!(put_request_cached.opts_len(), 0);
734
2
    }
735

            
736
    #[test]
737
2
    fn test_messages_to_user_ctor_owned() {
738
2
        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
739
2
        let put_request = PutRequestOwned::new_msgs_to_user_only(DEST_ID.into(), &[msg_to_user])
740
2
            .expect("creating msgs to user only put request failed");
741
2
        let msg_to_user_iter = put_request.msgs_to_user();
742
2
        assert!(msg_to_user_iter.is_some());
743
2
        assert!(put_request.check_tlv_type_validities());
744
2
        let msg_to_user_iter = msg_to_user_iter.unwrap();
745
4
        for msg_to_user_tlv in msg_to_user_iter {
746
2
            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
747
2
            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
748
        }
749
2
    }
750

            
751
    #[test]
752
2
    fn test_messages_to_user_ctor() {
753
2
        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
754
2
        let binding = &[msg_to_user.to_tlv()];
755
2
        let put_request = PutRequest::new_msgs_to_user_only(DEST_ID.into(), binding)
756
2
            .expect("creating msgs to user only put request failed");
757
2
        let msg_to_user_iter = put_request.msgs_to_user();
758
2
        assert!(put_request.check_tlv_type_validities());
759
2
        assert!(msg_to_user_iter.is_some());
760
2
        let msg_to_user_iter = msg_to_user_iter.unwrap();
761
4
        for msg_to_user_tlv in msg_to_user_iter {
762
2
            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
763
2
            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
764
        }
765
2
    }
766

            
767
    #[test]
768
2
    fn test_put_request_to_owned() {
769
2
        let src_file = "/tmp/hello.txt";
770
2
        let dest_file = "/tmp/hello2.txt";
771
2
        let put_request =
772
2
            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
773
2
                .unwrap();
774
2
        let put_request_owned: PutRequestOwned = put_request.into();
775
2
        assert_eq!(put_request_owned.destination_id(), DEST_ID.into());
776
2
        assert_eq!(put_request_owned.source_file().unwrap(), src_file);
777
2
        assert_eq!(put_request_owned.dest_file().unwrap(), dest_file);
778
2
        assert!(put_request_owned.msgs_to_user().is_none());
779
2
        assert!(put_request_owned.trans_mode().is_none());
780
2
        assert!(put_request_owned.closure_requested().is_none());
781
2
    }
782
}