Skip to main content

leodos_protocols/transport/cfdp/pdu/tlv/
filestore_request.rs

1use zerocopy::FromBytes;
2use zerocopy::Immutable;
3use zerocopy::IntoBytes;
4use zerocopy::KnownLayout;
5use zerocopy::Unaligned;
6
7use crate::transport::cfdp::CfdpError;
8use crate::transport::cfdp::filestore::FileId;
9use crate::transport::cfdp::pdu::tlv::FilestoreAction;
10use crate::transport::cfdp::pdu::tlv::Tlv;
11use crate::utils::get_bits_u8;
12use crate::utils::set_bits_u8;
13
14/// A zero-copy view of the Value of a Filestore Request TLV.
15#[repr(C)]
16#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
17pub struct TlvFilestoreRequest {
18    /// Packed byte with the 4-bit action code and 4 spare bits.
19    action_code_and_spare: u8,
20    /// Contains LV-encoded file names.
21    rest: [u8],
22}
23
24/// A parsed filestore request containing the action and file names.
25#[derive(Debug, PartialEq, Eq, Clone)]
26pub struct FilestoreRequest {
27    /// The filestore action to perform.
28    pub action: FilestoreAction,
29    /// The primary file name for the action.
30    pub first_file_name: FileId,
31    /// The secondary file name, required for rename/append/replace actions.
32    pub second_file_name: Option<FileId>,
33}
34
35#[rustfmt::skip]
36/// Bit masks for the filestore request's packed byte.
37mod bitmasks {
38    /// Mask for the 4-bit action code.
39    pub const ACTION_CODE_MASK: u8 = 0b1111_0000;
40    /// Mask for the 4-bit spare field (unused).
41    pub const _SPARE_MASK: u8 =      0b0000_1111;
42}
43
44impl TlvFilestoreRequest {
45    /// Parses a `TlvFilestoreRequest` from a generic `Tlv` reference.
46    pub fn from_tlv(tlv: &Tlv) -> Result<&Self, CfdpError> {
47        if tlv.length() < 1 {
48            return Err(CfdpError::Custom("Filestore Request TLV too short"));
49        }
50        TlvFilestoreRequest::ref_from_bytes(tlv.value())
51            .map_err(|_| CfdpError::Custom("Failed to parse Filestore Request TLV"))
52    }
53
54    /// Returns the filestore action code from the packed byte.
55    pub fn action(&self) -> Result<FilestoreAction, CfdpError> {
56        get_bits_u8(self.action_code_and_spare, bitmasks::ACTION_CODE_MASK).try_into()
57    }
58
59    /// Sets the filestore action code.
60    pub fn set_action(&mut self, action: FilestoreAction) {
61        set_bits_u8(
62            &mut self.action_code_and_spare,
63            bitmasks::ACTION_CODE_MASK,
64            action as u8,
65        );
66    }
67
68    /// Parses and returns the first file name.
69    pub fn first_file_name(&self) -> Result<&[u8], CfdpError> {
70        let len = *self
71            .rest
72            .first()
73            .ok_or(CfdpError::Custom("Missing first file name length"))? as usize;
74        self.rest
75            .get(1..1 + len)
76            .ok_or(CfdpError::Custom("Invalid first file name slice"))
77    }
78
79    /// Parses and returns the second file name, if present for the action type.
80    pub fn second_file_name(&self) -> Result<Option<&[u8]>, CfdpError> {
81        let has_second = matches!(
82            self.action()?,
83            FilestoreAction::RenameFile
84                | FilestoreAction::AppendFile
85                | FilestoreAction::ReplaceFile
86        );
87        if !has_second {
88            return Ok(None);
89        }
90
91        let first_lv_len = 1 + self.first_file_name()?.len();
92        let remainder = self
93            .rest
94            .get(first_lv_len..)
95            .ok_or(CfdpError::Custom("Invalid slice after first file name"))?;
96
97        let len = *remainder
98            .first()
99            .ok_or(CfdpError::Custom("Missing second file name length"))?
100            as usize;
101        let name = remainder
102            .get(1..1 + len)
103            .ok_or(CfdpError::Custom("Invalid second file name slice"))?;
104        Ok(Some(name))
105    }
106}