cfdp/
request.rs

1use core::str::Utf8Error;
2
3use spacepackets::{
4    ByteConversionError,
5    cfdp::{
6        SegmentationControl, TransmissionMode,
7        tlv::{GenericTlv, ReadableTlv as _, Tlv, TlvType, WritableTlv as _},
8    },
9    util::UnsignedByteField,
10};
11
12#[cfg(feature = "alloc")]
13pub use alloc_mod::*;
14
15#[derive(Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct FilePathTooLarge(pub usize);
19
20/// This trait is an abstraction for different Put Request structures which can be used
21/// by Put Request consumers.
22pub trait ReadablePutRequest {
23    fn destination_id(&self) -> UnsignedByteField;
24    fn source_file(&self) -> Option<&str>;
25    fn dest_file(&self) -> Option<&str>;
26    fn trans_mode(&self) -> Option<TransmissionMode>;
27    fn closure_requested(&self) -> Option<bool>;
28    fn seg_ctrl(&self) -> Option<SegmentationControl>;
29
30    fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
31    fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
32    fn flow_label(&self) -> Option<Tlv<'_>>;
33    fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
34}
35
36#[derive(Debug, PartialEq, Eq)]
37pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> {
38    pub destination_id: UnsignedByteField,
39    source_file: Option<&'src_file str>,
40    dest_file: Option<&'dest_file str>,
41    pub trans_mode: Option<TransmissionMode>,
42    pub closure_requested: Option<bool>,
43    pub seg_ctrl: Option<SegmentationControl>,
44    pub msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
45    pub fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
46    pub flow_label: Option<Tlv<'flow_label>>,
47    pub fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
48}
49
50impl<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
51    PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
52{
53    #[allow(clippy::too_many_arguments)]
54    pub fn new(
55        destination_id: UnsignedByteField,
56        source_file: Option<&'src_file str>,
57        dest_file: Option<&'dest_file str>,
58        trans_mode: Option<TransmissionMode>,
59        closure_requested: Option<bool>,
60        seg_ctrl: Option<SegmentationControl>,
61        msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
62        fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
63        flow_label: Option<Tlv<'flow_label>>,
64        fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
65    ) -> Result<Self, FilePathTooLarge> {
66        generic_path_checks(source_file, dest_file)?;
67        Ok(Self {
68            destination_id,
69            source_file,
70            dest_file,
71            trans_mode,
72            closure_requested,
73            seg_ctrl,
74            msgs_to_user,
75            fault_handler_overrides,
76            flow_label,
77            fs_requests,
78        })
79    }
80}
81
82impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> {
83    fn destination_id(&self) -> UnsignedByteField {
84        self.destination_id
85    }
86
87    fn source_file(&self) -> Option<&str> {
88        self.source_file
89    }
90
91    fn dest_file(&self) -> Option<&str> {
92        self.dest_file
93    }
94
95    fn trans_mode(&self) -> Option<TransmissionMode> {
96        self.trans_mode
97    }
98
99    fn closure_requested(&self) -> Option<bool> {
100        self.closure_requested
101    }
102
103    fn seg_ctrl(&self) -> Option<SegmentationControl> {
104        self.seg_ctrl
105    }
106
107    fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
108        if let Some(msgs_to_user) = self.msgs_to_user {
109            return Some(msgs_to_user.iter().copied());
110        }
111        None
112    }
113
114    fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
115        if let Some(fh_overrides) = self.fault_handler_overrides {
116            return Some(fh_overrides.iter().copied());
117        }
118        None
119    }
120
121    fn flow_label(&self) -> Option<Tlv<'_>> {
122        self.flow_label
123    }
124
125    fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
126        if let Some(fs_requests) = self.msgs_to_user {
127            return Some(fs_requests.iter().copied());
128        }
129        None
130    }
131}
132
133pub fn generic_path_checks(
134    source_file: Option<&str>,
135    dest_file: Option<&str>,
136) -> Result<(), FilePathTooLarge> {
137    if let Some(src_file) = source_file {
138        if src_file.len() > u8::MAX as usize {
139            return Err(FilePathTooLarge(src_file.len()));
140        }
141    }
142    if let Some(dest_file) = dest_file {
143        if dest_file.len() > u8::MAX as usize {
144            return Err(FilePathTooLarge(dest_file.len()));
145        }
146    }
147    Ok(())
148}
149
150impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> {
151    pub fn new_regular_request(
152        dest_id: UnsignedByteField,
153        source_file: &'src_file str,
154        dest_file: &'dest_file str,
155        trans_mode: Option<TransmissionMode>,
156        closure_requested: Option<bool>,
157    ) -> Result<Self, FilePathTooLarge> {
158        generic_path_checks(Some(source_file), Some(dest_file))?;
159        Ok(Self {
160            destination_id: dest_id,
161            source_file: Some(source_file),
162            dest_file: Some(dest_file),
163            trans_mode,
164            closure_requested,
165            seg_ctrl: None,
166            msgs_to_user: None,
167            fault_handler_overrides: None,
168            flow_label: None,
169            fs_requests: None,
170        })
171    }
172}
173
174#[derive(Debug, PartialEq, Eq)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct TlvWithInvalidType(pub(crate) ());
178
179impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> {
180    pub fn new_msgs_to_user_only(
181        dest_id: UnsignedByteField,
182        msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>],
183    ) -> Result<Self, TlvWithInvalidType> {
184        Ok(Self {
185            destination_id: dest_id,
186            source_file: None,
187            dest_file: None,
188            trans_mode: None,
189            closure_requested: None,
190            seg_ctrl: None,
191            msgs_to_user: Some(msgs_to_user),
192            fault_handler_overrides: None,
193            flow_label: None,
194            fs_requests: None,
195        })
196    }
197
198    /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
199    pub fn check_tlv_type_validities(&self) -> bool {
200        generic_tlv_list_type_check(self.msgs_to_user, TlvType::MsgToUser);
201        if let Some(flow_label) = &self.flow_label {
202            if flow_label.tlv_type().is_none() {
203                return false;
204            }
205            if flow_label.tlv_type().unwrap() != TlvType::FlowLabel {
206                return false;
207            }
208        }
209        generic_tlv_list_type_check(self.fault_handler_overrides, TlvType::FaultHandler);
210        generic_tlv_list_type_check(self.fs_requests, TlvType::FilestoreRequest);
211        true
212    }
213}
214
215pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
216    opt_tlvs: Option<&[TlvProvider]>,
217    tlv_type: TlvType,
218) -> bool {
219    if let Some(tlvs) = opt_tlvs {
220        for tlv in tlvs {
221            if tlv.tlv_type().is_none() {
222                return false;
223            }
224            if tlv.tlv_type().unwrap() != tlv_type {
225                return false;
226            }
227        }
228    }
229    true
230}
231
232pub struct StaticPutRequestFields {
233    pub destination_id: UnsignedByteField,
234    /// Static buffer to store source file path.
235    pub source_file_buf: [u8; u8::MAX as usize],
236    /// Current source path length.
237    pub source_file_len: usize,
238    /// Static buffer to store dest file path.
239    pub dest_file_buf: [u8; u8::MAX as usize],
240    /// Current destination path length.
241    pub dest_file_len: usize,
242    pub trans_mode: Option<TransmissionMode>,
243    pub closure_requested: Option<bool>,
244    pub seg_ctrl: Option<SegmentationControl>,
245}
246
247impl Default for StaticPutRequestFields {
248    fn default() -> Self {
249        Self {
250            destination_id: UnsignedByteField::new(0, 0),
251            source_file_buf: [0; u8::MAX as usize],
252            source_file_len: Default::default(),
253            dest_file_buf: [0; u8::MAX as usize],
254            dest_file_len: Default::default(),
255            trans_mode: Default::default(),
256            closure_requested: Default::default(),
257            seg_ctrl: Default::default(),
258        }
259    }
260}
261
262impl StaticPutRequestFields {
263    pub fn clear(&mut self) {
264        self.destination_id = UnsignedByteField::new(0, 0);
265        self.source_file_len = 0;
266        self.dest_file_len = 0;
267        self.trans_mode = None;
268        self.closure_requested = None;
269        self.seg_ctrl = None;
270    }
271}
272
273/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
274/// without requiring run-time allocation. The user must specify the static buffer sizes used
275/// to store TLVs or list of TLVs.
276pub struct StaticPutRequestCacher<const BUF_SIZE: usize> {
277    pub static_fields: StaticPutRequestFields,
278    opts_buf: [u8; BUF_SIZE],
279    opts_len: usize,
280}
281
282impl<const BUF_SIZE: usize> Default for StaticPutRequestCacher<BUF_SIZE> {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
289    pub fn new() -> Self {
290        Self {
291            static_fields: StaticPutRequestFields::default(),
292            opts_buf: [0; BUF_SIZE],
293            opts_len: 0,
294        }
295    }
296
297    pub fn set(
298        &mut self,
299        put_request: &impl ReadablePutRequest,
300    ) -> Result<(), ByteConversionError> {
301        self.static_fields.destination_id = put_request.destination_id();
302        if let Some(source_file) = put_request.source_file() {
303            if source_file.len() > u8::MAX as usize {
304                return Err(ByteConversionError::ToSliceTooSmall {
305                    found: self.static_fields.source_file_buf.len(),
306                    expected: source_file.len(),
307                });
308            }
309            self.static_fields.source_file_buf[..source_file.len()]
310                .copy_from_slice(source_file.as_bytes());
311            self.static_fields.source_file_len = source_file.len();
312        }
313        if let Some(dest_file) = put_request.dest_file() {
314            if dest_file.len() > u8::MAX as usize {
315                return Err(ByteConversionError::ToSliceTooSmall {
316                    found: self.static_fields.source_file_buf.len(),
317                    expected: dest_file.len(),
318                });
319            }
320            self.static_fields.dest_file_buf[..dest_file.len()]
321                .copy_from_slice(dest_file.as_bytes());
322            self.static_fields.dest_file_len = dest_file.len();
323        }
324        self.static_fields.trans_mode = put_request.trans_mode();
325        self.static_fields.closure_requested = put_request.closure_requested();
326        self.static_fields.seg_ctrl = put_request.seg_ctrl();
327        let mut current_idx = 0;
328        let mut store_tlv = |tlv: &Tlv| {
329            if current_idx + tlv.len_full() > self.opts_buf.len() {
330                return Err(ByteConversionError::ToSliceTooSmall {
331                    found: self.opts_buf.len(),
332                    expected: current_idx + tlv.len_full(),
333                });
334            }
335            // We checked the buffer lengths, so this should never fail.
336            tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
337                .unwrap();
338            current_idx += tlv.len_full();
339            Ok(())
340        };
341        if let Some(fs_req) = put_request.fs_requests() {
342            for fs_req in fs_req {
343                store_tlv(&fs_req)?;
344            }
345        }
346        if let Some(msgs_to_user) = put_request.msgs_to_user() {
347            for msg_to_user in msgs_to_user {
348                store_tlv(&msg_to_user)?;
349            }
350        }
351        self.opts_len = current_idx;
352        Ok(())
353    }
354
355    pub fn has_source_file(&self) -> bool {
356        self.static_fields.source_file_len > 0
357    }
358
359    pub fn has_dest_file(&self) -> bool {
360        self.static_fields.dest_file_len > 0
361    }
362
363    pub fn source_file(&self) -> Result<&str, Utf8Error> {
364        core::str::from_utf8(
365            &self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
366        )
367    }
368
369    pub fn dest_file(&self) -> Result<&str, Utf8Error> {
370        core::str::from_utf8(&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len])
371    }
372
373    pub fn opts_len(&self) -> usize {
374        self.opts_len
375    }
376
377    pub fn opts_slice(&self) -> &[u8] {
378        &self.opts_buf[0..self.opts_len]
379    }
380
381    /// This clears the cacher structure. This is a cheap operation because it only
382    /// sets [Option]al values to [None] and the length of stores TLVs to 0.
383    ///
384    /// Please note that this method will not set the values in the buffer to 0.
385    pub fn clear(&mut self) {
386        self.static_fields.clear();
387        self.opts_len = 0;
388    }
389}
390
391#[cfg(feature = "alloc")]
392pub mod alloc_mod {
393
394    use super::*;
395    use alloc::string::ToString;
396    use spacepackets::cfdp::tlv::{TlvOwned, msg_to_user::MsgToUserTlv};
397
398    /// Owned variant of [PutRequest] with no lifetimes which is also [Clone]able.
399    #[derive(Debug, Clone, PartialEq, Eq)]
400    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
401    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
402    pub struct PutRequestOwned {
403        pub destination_id: UnsignedByteField,
404        source_file: Option<alloc::string::String>,
405        dest_file: Option<alloc::string::String>,
406        pub trans_mode: Option<TransmissionMode>,
407        pub closure_requested: Option<bool>,
408        pub seg_ctrl: Option<SegmentationControl>,
409        pub msgs_to_user: Option<alloc::vec::Vec<TlvOwned>>,
410        pub fault_handler_overrides: Option<alloc::vec::Vec<TlvOwned>>,
411        pub flow_label: Option<TlvOwned>,
412        pub fs_requests: Option<alloc::vec::Vec<TlvOwned>>,
413    }
414
415    impl PutRequestOwned {
416        pub fn new_regular_request(
417            dest_id: UnsignedByteField,
418            source_file: &str,
419            dest_file: &str,
420            trans_mode: Option<TransmissionMode>,
421            closure_requested: Option<bool>,
422        ) -> Result<Self, FilePathTooLarge> {
423            if source_file.len() > u8::MAX as usize {
424                return Err(FilePathTooLarge(source_file.len()));
425            }
426            if dest_file.len() > u8::MAX as usize {
427                return Err(FilePathTooLarge(dest_file.len()));
428            }
429            Ok(Self {
430                destination_id: dest_id,
431                source_file: Some(source_file.to_string()),
432                dest_file: Some(dest_file.to_string()),
433                trans_mode,
434                closure_requested,
435                seg_ctrl: None,
436                msgs_to_user: None,
437                fault_handler_overrides: None,
438                flow_label: None,
439                fs_requests: None,
440            })
441        }
442
443        pub fn new_msgs_to_user_only(
444            dest_id: UnsignedByteField,
445            msgs_to_user: &[MsgToUserTlv<'_>],
446        ) -> Result<Self, TlvWithInvalidType> {
447            Ok(Self {
448                destination_id: dest_id,
449                source_file: None,
450                dest_file: None,
451                trans_mode: None,
452                closure_requested: None,
453                seg_ctrl: None,
454                msgs_to_user: Some(msgs_to_user.iter().map(|msg| msg.tlv.to_owned()).collect()),
455                fault_handler_overrides: None,
456                flow_label: None,
457                fs_requests: None,
458            })
459        }
460
461        /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
462        pub fn check_tlv_type_validities(&self) -> bool {
463            generic_tlv_list_type_check(self.msgs_to_user.as_deref(), TlvType::MsgToUser);
464            if let Some(flow_label) = &self.flow_label {
465                if flow_label.tlv_type().is_none() {
466                    return false;
467                }
468                if flow_label.tlv_type().unwrap() != TlvType::FlowLabel {
469                    return false;
470                }
471            }
472            generic_tlv_list_type_check(
473                self.fault_handler_overrides.as_deref(),
474                TlvType::FaultHandler,
475            );
476            generic_tlv_list_type_check(self.fs_requests.as_deref(), TlvType::FilestoreRequest);
477            true
478        }
479    }
480
481    impl From<PutRequest<'_, '_, '_, '_, '_, '_>> for PutRequestOwned {
482        fn from(req: PutRequest<'_, '_, '_, '_, '_, '_>) -> Self {
483            Self {
484                destination_id: req.destination_id,
485                source_file: req.source_file.map(|s| s.into()),
486                dest_file: req.dest_file.map(|s| s.into()),
487                trans_mode: req.trans_mode,
488                closure_requested: req.closure_requested,
489                seg_ctrl: req.seg_ctrl,
490                msgs_to_user: req
491                    .msgs_to_user
492                    .map(|msgs_to_user| msgs_to_user.iter().map(|msg| msg.to_owned()).collect()),
493                fault_handler_overrides: req
494                    .msgs_to_user
495                    .map(|fh_overides| fh_overides.iter().map(|msg| msg.to_owned()).collect()),
496                flow_label: req
497                    .flow_label
498                    .map(|flow_label_tlv| flow_label_tlv.to_owned()),
499                fs_requests: req
500                    .fs_requests
501                    .map(|fs_requests| fs_requests.iter().map(|msg| msg.to_owned()).collect()),
502            }
503        }
504    }
505
506    impl ReadablePutRequest for PutRequestOwned {
507        fn destination_id(&self) -> UnsignedByteField {
508            self.destination_id
509        }
510
511        fn source_file(&self) -> Option<&str> {
512            self.source_file.as_deref()
513        }
514
515        fn dest_file(&self) -> Option<&str> {
516            self.dest_file.as_deref()
517        }
518
519        fn trans_mode(&self) -> Option<TransmissionMode> {
520            self.trans_mode
521        }
522
523        fn closure_requested(&self) -> Option<bool> {
524            self.closure_requested
525        }
526
527        fn seg_ctrl(&self) -> Option<SegmentationControl> {
528            self.seg_ctrl
529        }
530
531        fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
532            if let Some(msgs_to_user) = &self.msgs_to_user {
533                return Some(msgs_to_user.iter().map(|tlv_owned| tlv_owned.as_tlv()));
534            }
535            None
536        }
537
538        fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
539            if let Some(fh_overrides) = &self.fault_handler_overrides {
540                return Some(fh_overrides.iter().map(|tlv_owned| tlv_owned.as_tlv()));
541            }
542            None
543        }
544
545        fn flow_label(&self) -> Option<Tlv<'_>> {
546            self.flow_label.as_ref().map(|tlv| tlv.as_tlv())
547        }
548
549        fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
550            if let Some(requests) = &self.fs_requests {
551                return Some(requests.iter().map(|tlv_owned| tlv_owned.as_tlv()));
552            }
553            None
554        }
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use std::string::String;
561
562    use spacepackets::{
563        cfdp::tlv::{ReadableTlv, msg_to_user::MsgToUserTlv},
564        util::UbfU16,
565    };
566
567    use super::*;
568
569    pub const DEST_ID: UbfU16 = UbfU16::new(5);
570
571    #[test]
572    fn test_put_request_basic() {
573        let src_file = "/tmp/hello.txt";
574        let dest_file = "/tmp/hello2.txt";
575        let put_request = PutRequest::new(
576            DEST_ID.into(),
577            Some(src_file),
578            Some(dest_file),
579            None,
580            None,
581            None,
582            None,
583            None,
584            None,
585            None,
586        )
587        .unwrap();
588        let identical_request =
589            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
590                .unwrap();
591        assert_eq!(put_request, identical_request);
592    }
593
594    #[test]
595    fn test_put_request_path_checks_source_too_long() {
596        let mut invalid_path = String::from("/tmp/");
597        invalid_path += "a".repeat(u8::MAX as usize).as_str();
598        let dest_file = "/tmp/hello2.txt";
599        let error =
600            PutRequest::new_regular_request(DEST_ID.into(), &invalid_path, dest_file, None, None);
601        assert!(error.is_err());
602        let error = error.unwrap_err();
603        assert_eq!(u8::MAX as usize + 5, error.0);
604    }
605
606    #[test]
607    fn test_put_request_path_checks_dest_file_too_long() {
608        let mut invalid_path = String::from("/tmp/");
609        invalid_path += "a".repeat(u8::MAX as usize).as_str();
610        let source_file = "/tmp/hello2.txt";
611        let error =
612            PutRequest::new_regular_request(DEST_ID.into(), source_file, &invalid_path, None, None);
613        assert!(error.is_err());
614        let error = error.unwrap_err();
615        assert_eq!(u8::MAX as usize + 5, error.0);
616    }
617
618    #[test]
619    fn test_owned_put_request_path_checks_source_too_long() {
620        let mut invalid_path = String::from("/tmp/");
621        invalid_path += "a".repeat(u8::MAX as usize).as_str();
622        let dest_file = "/tmp/hello2.txt";
623        let error = PutRequestOwned::new_regular_request(
624            DEST_ID.into(),
625            &invalid_path,
626            dest_file,
627            None,
628            None,
629        );
630        assert!(error.is_err());
631        let error = error.unwrap_err();
632        assert_eq!(u8::MAX as usize + 5, error.0);
633    }
634
635    #[test]
636    fn test_owned_put_request_path_checks_dest_file_too_long() {
637        let mut invalid_path = String::from("/tmp/");
638        invalid_path += "a".repeat(u8::MAX as usize).as_str();
639        let source_file = "/tmp/hello2.txt";
640        let error = PutRequestOwned::new_regular_request(
641            DEST_ID.into(),
642            source_file,
643            &invalid_path,
644            None,
645            None,
646        );
647        assert!(error.is_err());
648        let error = error.unwrap_err();
649        assert_eq!(u8::MAX as usize + 5, error.0);
650    }
651
652    #[test]
653    fn test_put_request_basic_small_ctor() {
654        let src_file = "/tmp/hello.txt";
655        let dest_file = "/tmp/hello2.txt";
656        let put_request =
657            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
658                .unwrap();
659        assert_eq!(put_request.source_file(), Some(src_file));
660        assert_eq!(put_request.dest_file(), Some(dest_file));
661        assert_eq!(put_request.destination_id(), DEST_ID.into());
662        assert_eq!(put_request.seg_ctrl(), None);
663        assert_eq!(put_request.closure_requested(), None);
664        assert_eq!(put_request.trans_mode(), None);
665        assert!(put_request.fs_requests().is_none());
666        assert!(put_request.msgs_to_user().is_none());
667        assert!(put_request.fault_handler_overrides().is_none());
668        assert!(put_request.flow_label().is_none());
669    }
670
671    #[test]
672    fn test_put_request_owned_basic() {
673        let src_file = "/tmp/hello.txt";
674        let dest_file = "/tmp/hello2.txt";
675        let put_request =
676            PutRequestOwned::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
677                .unwrap();
678        assert_eq!(put_request.source_file(), Some(src_file));
679        assert_eq!(put_request.dest_file(), Some(dest_file));
680        assert_eq!(put_request.destination_id(), DEST_ID.into());
681        assert_eq!(put_request.seg_ctrl(), None);
682        assert_eq!(put_request.closure_requested(), None);
683        assert_eq!(put_request.trans_mode(), None);
684        assert!(put_request.flow_label().is_none());
685        assert!(put_request.fs_requests().is_none());
686        assert!(put_request.msgs_to_user().is_none());
687        assert!(put_request.fault_handler_overrides().is_none());
688        assert!(put_request.flow_label().is_none());
689        let put_request_cloned = put_request.clone();
690        assert_eq!(put_request, put_request_cloned);
691    }
692
693    #[test]
694    fn test_put_request_cacher_basic() {
695        let put_request_cached = StaticPutRequestCacher::<128>::new();
696        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
697        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
698        assert_eq!(put_request_cached.opts_len(), 0);
699        assert_eq!(put_request_cached.opts_slice(), &[]);
700    }
701
702    #[test]
703    fn test_put_request_cacher_set() {
704        let mut put_request_cached = StaticPutRequestCacher::<128>::new();
705        let src_file = "/tmp/hello.txt";
706        let dest_file = "/tmp/hello2.txt";
707        let put_request =
708            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
709                .unwrap();
710        put_request_cached.set(&put_request).unwrap();
711        assert_eq!(
712            put_request_cached.static_fields.source_file_len,
713            src_file.len()
714        );
715        assert_eq!(
716            put_request_cached.static_fields.dest_file_len,
717            dest_file.len()
718        );
719        assert_eq!(put_request_cached.source_file().unwrap(), src_file);
720        assert_eq!(put_request_cached.dest_file().unwrap(), dest_file);
721        assert_eq!(put_request_cached.opts_len(), 0);
722    }
723
724    #[test]
725    fn test_put_request_cacher_set_and_clear() {
726        let mut put_request_cached = StaticPutRequestCacher::<128>::new();
727        let src_file = "/tmp/hello.txt";
728        let dest_file = "/tmp/hello2.txt";
729        let put_request =
730            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
731                .unwrap();
732        put_request_cached.set(&put_request).unwrap();
733        put_request_cached.clear();
734        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
735        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
736        assert_eq!(put_request_cached.opts_len(), 0);
737    }
738
739    #[test]
740    fn test_messages_to_user_ctor_owned() {
741        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
742        let put_request = PutRequestOwned::new_msgs_to_user_only(DEST_ID.into(), &[msg_to_user])
743            .expect("creating msgs to user only put request failed");
744        let msg_to_user_iter = put_request.msgs_to_user();
745        assert!(msg_to_user_iter.is_some());
746        assert!(put_request.check_tlv_type_validities());
747        let msg_to_user_iter = msg_to_user_iter.unwrap();
748        for msg_to_user_tlv in msg_to_user_iter {
749            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
750            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
751        }
752    }
753
754    #[test]
755    fn test_messages_to_user_ctor() {
756        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
757        let binding = &[msg_to_user.to_tlv()];
758        let put_request = PutRequest::new_msgs_to_user_only(DEST_ID.into(), binding)
759            .expect("creating msgs to user only put request failed");
760        let msg_to_user_iter = put_request.msgs_to_user();
761        assert!(put_request.check_tlv_type_validities());
762        assert!(msg_to_user_iter.is_some());
763        let msg_to_user_iter = msg_to_user_iter.unwrap();
764        for msg_to_user_tlv in msg_to_user_iter {
765            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
766            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
767        }
768    }
769
770    #[test]
771    fn test_put_request_to_owned() {
772        let src_file = "/tmp/hello.txt";
773        let dest_file = "/tmp/hello2.txt";
774        let put_request =
775            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
776                .unwrap();
777        let put_request_owned: PutRequestOwned = put_request.into();
778        assert_eq!(put_request_owned.destination_id(), DEST_ID.into());
779        assert_eq!(put_request_owned.source_file().unwrap(), src_file);
780        assert_eq!(put_request_owned.dest_file().unwrap(), dest_file);
781        assert!(put_request_owned.msgs_to_user().is_none());
782        assert!(put_request_owned.trans_mode().is_none());
783        assert!(put_request_owned.closure_requested().is_none());
784    }
785}