cfdp/
request.rs

1use spacepackets::{
2    cfdp::{
3        tlv::{GenericTlv, Tlv, TlvType},
4        SegmentationControl, TransmissionMode,
5    },
6    util::UnsignedByteField,
7};
8
9#[cfg(feature = "alloc")]
10pub 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))]
15pub 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.
19pub 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)]
34pub 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
47impl<'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    pub fn new(
52        destination_id: UnsignedByteField,
53        source_file: Option<&'src_file str>,
54        dest_file: Option<&'dest_file str>,
55        trans_mode: Option<TransmissionMode>,
56        closure_requested: Option<bool>,
57        seg_ctrl: Option<SegmentationControl>,
58        msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
59        fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
60        flow_label: Option<Tlv<'flow_label>>,
61        fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
62    ) -> Result<Self, FilePathTooLarge> {
63        generic_path_checks(source_file, dest_file)?;
64        Ok(Self {
65            destination_id,
66            source_file,
67            dest_file,
68            trans_mode,
69            closure_requested,
70            seg_ctrl,
71            msgs_to_user,
72            fault_handler_overrides,
73            flow_label,
74            fs_requests,
75        })
76    }
77}
78
79impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> {
80    fn destination_id(&self) -> UnsignedByteField {
81        self.destination_id
82    }
83
84    fn source_file(&self) -> Option<&str> {
85        self.source_file
86    }
87
88    fn dest_file(&self) -> Option<&str> {
89        self.dest_file
90    }
91
92    fn trans_mode(&self) -> Option<TransmissionMode> {
93        self.trans_mode
94    }
95
96    fn closure_requested(&self) -> Option<bool> {
97        self.closure_requested
98    }
99
100    fn seg_ctrl(&self) -> Option<SegmentationControl> {
101        self.seg_ctrl
102    }
103
104    fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
105        if let Some(msgs_to_user) = self.msgs_to_user {
106            return Some(msgs_to_user.iter().copied());
107        }
108        None
109    }
110
111    fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
112        if let Some(fh_overrides) = self.fault_handler_overrides {
113            return Some(fh_overrides.iter().copied());
114        }
115        None
116    }
117
118    fn flow_label(&self) -> Option<Tlv> {
119        self.flow_label
120    }
121
122    fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
123        if let Some(fs_requests) = self.msgs_to_user {
124            return Some(fs_requests.iter().copied());
125        }
126        None
127    }
128}
129
130pub fn generic_path_checks(
131    source_file: Option<&str>,
132    dest_file: Option<&str>,
133) -> Result<(), FilePathTooLarge> {
134    if let Some(src_file) = source_file {
135        if src_file.len() > u8::MAX as usize {
136            return Err(FilePathTooLarge(src_file.len()));
137        }
138    }
139    if let Some(dest_file) = dest_file {
140        if dest_file.len() > u8::MAX as usize {
141            return Err(FilePathTooLarge(dest_file.len()));
142        }
143    }
144    Ok(())
145}
146
147impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> {
148    pub fn new_regular_request(
149        dest_id: UnsignedByteField,
150        source_file: &'src_file str,
151        dest_file: &'dest_file str,
152        trans_mode: Option<TransmissionMode>,
153        closure_requested: Option<bool>,
154    ) -> Result<Self, FilePathTooLarge> {
155        generic_path_checks(Some(source_file), Some(dest_file))?;
156        Ok(Self {
157            destination_id: dest_id,
158            source_file: Some(source_file),
159            dest_file: Some(dest_file),
160            trans_mode,
161            closure_requested,
162            seg_ctrl: None,
163            msgs_to_user: None,
164            fault_handler_overrides: None,
165            flow_label: None,
166            fs_requests: None,
167        })
168    }
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))]
174pub struct TlvWithInvalidType(pub(crate) ());
175
176impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> {
177    pub fn new_msgs_to_user_only(
178        dest_id: UnsignedByteField,
179        msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>],
180    ) -> Result<Self, TlvWithInvalidType> {
181        Ok(Self {
182            destination_id: dest_id,
183            source_file: None,
184            dest_file: None,
185            trans_mode: None,
186            closure_requested: None,
187            seg_ctrl: None,
188            msgs_to_user: Some(msgs_to_user),
189            fault_handler_overrides: None,
190            flow_label: None,
191            fs_requests: None,
192        })
193    }
194
195    /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
196    pub fn check_tlv_type_validities(&self) -> bool {
197        generic_tlv_list_type_check(self.msgs_to_user, TlvType::MsgToUser);
198        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        }
206        generic_tlv_list_type_check(self.fault_handler_overrides, TlvType::FaultHandler);
207        generic_tlv_list_type_check(self.fs_requests, TlvType::FilestoreRequest);
208        true
209    }
210}
211
212pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
213    opt_tlvs: Option<&[TlvProvider]>,
214    tlv_type: TlvType,
215) -> bool {
216    if let Some(tlvs) = opt_tlvs {
217        for tlv in tlvs {
218            if tlv.tlv_type().is_none() {
219                return false;
220            }
221            if tlv.tlv_type().unwrap() != tlv_type {
222                return false;
223            }
224        }
225    }
226    true
227}
228
229#[cfg(feature = "alloc")]
230pub 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        pub fn new_regular_request(
259            dest_id: UnsignedByteField,
260            source_file: &str,
261            dest_file: &str,
262            trans_mode: Option<TransmissionMode>,
263            closure_requested: Option<bool>,
264        ) -> Result<Self, FilePathTooLarge> {
265            if source_file.len() > u8::MAX as usize {
266                return Err(FilePathTooLarge(source_file.len()));
267            }
268            if dest_file.len() > u8::MAX as usize {
269                return Err(FilePathTooLarge(dest_file.len()));
270            }
271            Ok(Self {
272                destination_id: dest_id,
273                source_file: Some(source_file.to_string()),
274                dest_file: Some(dest_file.to_string()),
275                trans_mode,
276                closure_requested,
277                seg_ctrl: None,
278                msgs_to_user: None,
279                fault_handler_overrides: None,
280                flow_label: None,
281                fs_requests: None,
282            })
283        }
284
285        pub fn new_msgs_to_user_only(
286            dest_id: UnsignedByteField,
287            msgs_to_user: &[MsgToUserTlv<'_>],
288        ) -> Result<Self, TlvWithInvalidType> {
289            Ok(Self {
290                destination_id: dest_id,
291                source_file: None,
292                dest_file: None,
293                trans_mode: None,
294                closure_requested: None,
295                seg_ctrl: None,
296                msgs_to_user: Some(msgs_to_user.iter().map(|msg| msg.tlv.to_owned()).collect()),
297                fault_handler_overrides: None,
298                flow_label: None,
299                fs_requests: None,
300            })
301        }
302
303        /// Uses [generic_tlv_list_type_check] to check the TLV type validity of all TLV fields.
304        pub fn check_tlv_type_validities(&self) -> bool {
305            generic_tlv_list_type_check(self.msgs_to_user.as_deref(), TlvType::MsgToUser);
306            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            }
314            generic_tlv_list_type_check(
315                self.fault_handler_overrides.as_deref(),
316                TlvType::FaultHandler,
317            );
318            generic_tlv_list_type_check(self.fs_requests.as_deref(), TlvType::FilestoreRequest);
319            true
320        }
321    }
322
323    impl From<PutRequest<'_, '_, '_, '_, '_, '_>> for PutRequestOwned {
324        fn from(req: PutRequest<'_, '_, '_, '_, '_, '_>) -> Self {
325            Self {
326                destination_id: req.destination_id,
327                source_file: req.source_file.map(|s| s.into()),
328                dest_file: req.dest_file.map(|s| s.into()),
329                trans_mode: req.trans_mode,
330                closure_requested: req.closure_requested,
331                seg_ctrl: req.seg_ctrl,
332                msgs_to_user: req
333                    .msgs_to_user
334                    .map(|msgs_to_user| msgs_to_user.iter().map(|msg| msg.to_owned()).collect()),
335                fault_handler_overrides: req
336                    .msgs_to_user
337                    .map(|fh_overides| fh_overides.iter().map(|msg| msg.to_owned()).collect()),
338                flow_label: req
339                    .flow_label
340                    .map(|flow_label_tlv| flow_label_tlv.to_owned()),
341                fs_requests: req
342                    .fs_requests
343                    .map(|fs_requests| fs_requests.iter().map(|msg| msg.to_owned()).collect()),
344            }
345        }
346    }
347
348    impl ReadablePutRequest for PutRequestOwned {
349        fn destination_id(&self) -> UnsignedByteField {
350            self.destination_id
351        }
352
353        fn source_file(&self) -> Option<&str> {
354            self.source_file.as_deref()
355        }
356
357        fn dest_file(&self) -> Option<&str> {
358            self.dest_file.as_deref()
359        }
360
361        fn trans_mode(&self) -> Option<TransmissionMode> {
362            self.trans_mode
363        }
364
365        fn closure_requested(&self) -> Option<bool> {
366            self.closure_requested
367        }
368
369        fn seg_ctrl(&self) -> Option<SegmentationControl> {
370            self.seg_ctrl
371        }
372
373        fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
374            if let Some(msgs_to_user) = &self.msgs_to_user {
375                return Some(msgs_to_user.iter().map(|tlv_owned| tlv_owned.as_tlv()));
376            }
377            None
378        }
379
380        fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
381            if let Some(fh_overrides) = &self.fault_handler_overrides {
382                return Some(fh_overrides.iter().map(|tlv_owned| tlv_owned.as_tlv()));
383            }
384            None
385        }
386
387        fn flow_label(&self) -> Option<Tlv> {
388            self.flow_label.as_ref().map(|tlv| tlv.as_tlv())
389        }
390
391        fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
392            if let Some(requests) = &self.fs_requests {
393                return Some(requests.iter().map(|tlv_owned| tlv_owned.as_tlv()));
394            }
395            None
396        }
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        fn default() -> Self {
416            Self {
417                destination_id: UnsignedByteField::new(0, 0),
418                source_file_buf: [0; u8::MAX as usize],
419                source_file_len: Default::default(),
420                dest_file_buf: [0; u8::MAX as usize],
421                dest_file_len: Default::default(),
422                trans_mode: Default::default(),
423                closure_requested: Default::default(),
424                seg_ctrl: Default::default(),
425            }
426        }
427    }
428
429    impl StaticPutRequestFields {
430        pub fn clear(&mut self) {
431            self.destination_id = UnsignedByteField::new(0, 0);
432            self.source_file_len = 0;
433            self.dest_file_len = 0;
434            self.trans_mode = None;
435            self.closure_requested = None;
436            self.seg_ctrl = None;
437        }
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        pub fn new(max_len_opts_buf: usize) -> Self {
451            Self {
452                static_fields: StaticPutRequestFields::default(),
453                opts_buf: alloc::vec![0; max_len_opts_buf],
454                opts_len: 0,
455            }
456        }
457
458        pub fn set(
459            &mut self,
460            put_request: &impl ReadablePutRequest,
461        ) -> Result<(), ByteConversionError> {
462            self.static_fields.destination_id = put_request.destination_id();
463            if let Some(source_file) = put_request.source_file() {
464                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                }
470                self.static_fields.source_file_buf[..source_file.len()]
471                    .copy_from_slice(source_file.as_bytes());
472                self.static_fields.source_file_len = source_file.len();
473            }
474            if let Some(dest_file) = put_request.dest_file() {
475                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                }
481                self.static_fields.dest_file_buf[..dest_file.len()]
482                    .copy_from_slice(dest_file.as_bytes());
483                self.static_fields.dest_file_len = dest_file.len();
484            }
485            self.static_fields.trans_mode = put_request.trans_mode();
486            self.static_fields.closure_requested = put_request.closure_requested();
487            self.static_fields.seg_ctrl = put_request.seg_ctrl();
488            let mut current_idx = 0;
489            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            if let Some(fs_req) = put_request.fs_requests() {
503                for fs_req in fs_req {
504                    store_tlv(&fs_req)?;
505                }
506            }
507            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            }
512            self.opts_len = current_idx;
513            Ok(())
514        }
515
516        pub fn has_source_file(&self) -> bool {
517            self.static_fields.source_file_len > 0
518        }
519
520        pub fn has_dest_file(&self) -> bool {
521            self.static_fields.dest_file_len > 0
522        }
523
524        pub fn source_file(&self) -> Result<&str, Utf8Error> {
525            core::str::from_utf8(
526                &self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
527            )
528        }
529
530        pub fn dest_file(&self) -> Result<&str, Utf8Error> {
531            core::str::from_utf8(
532                &self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len],
533            )
534        }
535
536        pub fn opts_len(&self) -> usize {
537            self.opts_len
538        }
539
540        pub fn opts_slice(&self) -> &[u8] {
541            &self.opts_buf[0..self.opts_len]
542        }
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        pub fn clear(&mut self) {
549            self.static_fields.clear();
550            self.opts_len = 0;
551        }
552    }
553}
554
555#[cfg(test)]
556mod 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    fn test_put_request_basic() {
570        let src_file = "/tmp/hello.txt";
571        let dest_file = "/tmp/hello2.txt";
572        let put_request = PutRequest::new(
573            DEST_ID.into(),
574            Some(src_file),
575            Some(dest_file),
576            None,
577            None,
578            None,
579            None,
580            None,
581            None,
582            None,
583        )
584        .unwrap();
585        let identical_request =
586            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
587                .unwrap();
588        assert_eq!(put_request, identical_request);
589    }
590
591    #[test]
592    fn test_put_request_path_checks_source_too_long() {
593        let mut invalid_path = String::from("/tmp/");
594        invalid_path += "a".repeat(u8::MAX as usize).as_str();
595        let dest_file = "/tmp/hello2.txt";
596        let error =
597            PutRequest::new_regular_request(DEST_ID.into(), &invalid_path, dest_file, None, None);
598        assert!(error.is_err());
599        let error = error.unwrap_err();
600        assert_eq!(u8::MAX as usize + 5, error.0);
601    }
602
603    #[test]
604    fn test_put_request_path_checks_dest_file_too_long() {
605        let mut invalid_path = String::from("/tmp/");
606        invalid_path += "a".repeat(u8::MAX as usize).as_str();
607        let source_file = "/tmp/hello2.txt";
608        let error =
609            PutRequest::new_regular_request(DEST_ID.into(), source_file, &invalid_path, None, None);
610        assert!(error.is_err());
611        let error = error.unwrap_err();
612        assert_eq!(u8::MAX as usize + 5, error.0);
613    }
614
615    #[test]
616    fn test_owned_put_request_path_checks_source_too_long() {
617        let mut invalid_path = String::from("/tmp/");
618        invalid_path += "a".repeat(u8::MAX as usize).as_str();
619        let dest_file = "/tmp/hello2.txt";
620        let error = PutRequestOwned::new_regular_request(
621            DEST_ID.into(),
622            &invalid_path,
623            dest_file,
624            None,
625            None,
626        );
627        assert!(error.is_err());
628        let error = error.unwrap_err();
629        assert_eq!(u8::MAX as usize + 5, error.0);
630    }
631
632    #[test]
633    fn test_owned_put_request_path_checks_dest_file_too_long() {
634        let mut invalid_path = String::from("/tmp/");
635        invalid_path += "a".repeat(u8::MAX as usize).as_str();
636        let source_file = "/tmp/hello2.txt";
637        let error = PutRequestOwned::new_regular_request(
638            DEST_ID.into(),
639            source_file,
640            &invalid_path,
641            None,
642            None,
643        );
644        assert!(error.is_err());
645        let error = error.unwrap_err();
646        assert_eq!(u8::MAX as usize + 5, error.0);
647    }
648
649    #[test]
650    fn test_put_request_basic_small_ctor() {
651        let src_file = "/tmp/hello.txt";
652        let dest_file = "/tmp/hello2.txt";
653        let put_request =
654            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
655                .unwrap();
656        assert_eq!(put_request.source_file(), Some(src_file));
657        assert_eq!(put_request.dest_file(), Some(dest_file));
658        assert_eq!(put_request.destination_id(), DEST_ID.into());
659        assert_eq!(put_request.seg_ctrl(), None);
660        assert_eq!(put_request.closure_requested(), None);
661        assert_eq!(put_request.trans_mode(), None);
662        assert!(put_request.fs_requests().is_none());
663        assert!(put_request.msgs_to_user().is_none());
664        assert!(put_request.fault_handler_overrides().is_none());
665        assert!(put_request.flow_label().is_none());
666    }
667
668    #[test]
669    fn test_put_request_owned_basic() {
670        let src_file = "/tmp/hello.txt";
671        let dest_file = "/tmp/hello2.txt";
672        let put_request =
673            PutRequestOwned::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
674                .unwrap();
675        assert_eq!(put_request.source_file(), Some(src_file));
676        assert_eq!(put_request.dest_file(), Some(dest_file));
677        assert_eq!(put_request.destination_id(), DEST_ID.into());
678        assert_eq!(put_request.seg_ctrl(), None);
679        assert_eq!(put_request.closure_requested(), None);
680        assert_eq!(put_request.trans_mode(), None);
681        assert!(put_request.flow_label().is_none());
682        assert!(put_request.fs_requests().is_none());
683        assert!(put_request.msgs_to_user().is_none());
684        assert!(put_request.fault_handler_overrides().is_none());
685        assert!(put_request.flow_label().is_none());
686        let put_request_cloned = put_request.clone();
687        assert_eq!(put_request, put_request_cloned);
688    }
689
690    #[test]
691    fn test_put_request_cacher_basic() {
692        let put_request_cached = StaticPutRequestCacher::new(128);
693        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
694        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
695        assert_eq!(put_request_cached.opts_len(), 0);
696        assert_eq!(put_request_cached.opts_slice(), &[]);
697    }
698
699    #[test]
700    fn test_put_request_cacher_set() {
701        let mut put_request_cached = StaticPutRequestCacher::new(128);
702        let src_file = "/tmp/hello.txt";
703        let dest_file = "/tmp/hello2.txt";
704        let put_request =
705            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
706                .unwrap();
707        put_request_cached.set(&put_request).unwrap();
708        assert_eq!(
709            put_request_cached.static_fields.source_file_len,
710            src_file.len()
711        );
712        assert_eq!(
713            put_request_cached.static_fields.dest_file_len,
714            dest_file.len()
715        );
716        assert_eq!(put_request_cached.source_file().unwrap(), src_file);
717        assert_eq!(put_request_cached.dest_file().unwrap(), dest_file);
718        assert_eq!(put_request_cached.opts_len(), 0);
719    }
720
721    #[test]
722    fn test_put_request_cacher_set_and_clear() {
723        let mut put_request_cached = StaticPutRequestCacher::new(128);
724        let src_file = "/tmp/hello.txt";
725        let dest_file = "/tmp/hello2.txt";
726        let put_request =
727            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
728                .unwrap();
729        put_request_cached.set(&put_request).unwrap();
730        put_request_cached.clear();
731        assert_eq!(put_request_cached.static_fields.source_file_len, 0);
732        assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
733        assert_eq!(put_request_cached.opts_len(), 0);
734    }
735
736    #[test]
737    fn test_messages_to_user_ctor_owned() {
738        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
739        let put_request = PutRequestOwned::new_msgs_to_user_only(DEST_ID.into(), &[msg_to_user])
740            .expect("creating msgs to user only put request failed");
741        let msg_to_user_iter = put_request.msgs_to_user();
742        assert!(msg_to_user_iter.is_some());
743        assert!(put_request.check_tlv_type_validities());
744        let msg_to_user_iter = msg_to_user_iter.unwrap();
745        for msg_to_user_tlv in msg_to_user_iter {
746            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
747            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
748        }
749    }
750
751    #[test]
752    fn test_messages_to_user_ctor() {
753        let msg_to_user = MsgToUserTlv::new(&[1, 2, 3]).expect("creating message to user failed");
754        let binding = &[msg_to_user.to_tlv()];
755        let put_request = PutRequest::new_msgs_to_user_only(DEST_ID.into(), binding)
756            .expect("creating msgs to user only put request failed");
757        let msg_to_user_iter = put_request.msgs_to_user();
758        assert!(put_request.check_tlv_type_validities());
759        assert!(msg_to_user_iter.is_some());
760        let msg_to_user_iter = msg_to_user_iter.unwrap();
761        for msg_to_user_tlv in msg_to_user_iter {
762            assert_eq!(msg_to_user_tlv.value(), msg_to_user.value());
763            assert_eq!(msg_to_user_tlv.tlv_type().unwrap(), TlvType::MsgToUser);
764        }
765    }
766
767    #[test]
768    fn test_put_request_to_owned() {
769        let src_file = "/tmp/hello.txt";
770        let dest_file = "/tmp/hello2.txt";
771        let put_request =
772            PutRequest::new_regular_request(DEST_ID.into(), src_file, dest_file, None, None)
773                .unwrap();
774        let put_request_owned: PutRequestOwned = put_request.into();
775        assert_eq!(put_request_owned.destination_id(), DEST_ID.into());
776        assert_eq!(put_request_owned.source_file().unwrap(), src_file);
777        assert_eq!(put_request_owned.dest_file().unwrap(), dest_file);
778        assert!(put_request_owned.msgs_to_user().is_none());
779        assert!(put_request_owned.trans_mode().is_none());
780        assert!(put_request_owned.closure_requested().is_none());
781    }
782}