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
20pub 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 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 pub source_file_buf: [u8; u8::MAX as usize],
236 pub source_file_len: usize,
238 pub dest_file_buf: [u8; u8::MAX as usize],
240 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
273pub 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 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 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 #[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 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}