Skip to main content

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

1/// Entity ID TLV.
2pub mod entity_id;
3/// Fault Handler Override TLV and handler set.
4pub mod fault_handler_override;
5/// Filestore Request TLV.
6pub mod filestore_request;
7/// Filestore Response TLV.
8pub mod filestore_response;
9/// Flow Label TLV.
10pub mod flow_label;
11/// Message to User TLV.
12pub mod message_to_user;
13
14use zerocopy::FromBytes;
15use zerocopy::Immutable;
16use zerocopy::IntoBytes;
17use zerocopy::KnownLayout;
18use zerocopy::Unaligned;
19
20use crate::transport::cfdp::pdu::CfdpError;
21
22/// A single, unified, zero-copy view of a TLV record.
23/// It contains the Type, Length, and the variable-length Value.
24#[repr(C)]
25#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout)]
26pub struct Tlv {
27    /// The TLV type code byte.
28    tlv_type: u8,
29    /// The length of the value field in bytes.
30    length: u8,
31    /// The variable-length value payload.
32    value: [u8],
33}
34
35impl Tlv {
36    /// Returns the TLV's type.
37    pub fn tlv_type(&self) -> Result<TlvType, CfdpError> {
38        TlvType::try_from(self.tlv_type)
39    }
40    /// Sets the TLV's type field.
41    pub fn set_type(&mut self, tlv_type: TlvType) {
42        self.tlv_type = tlv_type as u8;
43    }
44
45    /// Returns the length of the value field in bytes.
46    pub fn length(&self) -> usize {
47        self.length as usize
48    }
49    /// Sets the length of the value field in bytes.
50    pub fn set_length(&mut self, length: usize) -> Result<(), CfdpError> {
51        if length > u8::MAX as usize {
52            return Err(CfdpError::Custom("Length exceeds maximum value for TLV"));
53        }
54        self.length = length as u8;
55        Ok(())
56    }
57
58    /// Returns an immutable slice of the value field.
59    pub fn value(&self) -> &[u8] {
60        &self.value
61    }
62    /// Writes the given bytes into the value field.
63    pub fn set_value(&mut self, value: &[u8]) -> Result<(), CfdpError> {
64        let len = value.len();
65        self.value
66            .get_mut(..len)
67            .ok_or(CfdpError::Custom("Value length exceeds allocated size"))?
68            .copy_from_slice(value);
69        Ok(())
70    }
71
72    /// Calculates the total length of this TLV instance in bytes.
73    pub fn total_len(&self) -> usize {
74        2 + self.length() // 2 bytes for Type and Length
75    }
76}
77
78/// Identifies the type of a TLV record (Table 5-14).
79#[derive(Debug, Copy, Clone, PartialEq, Eq)]
80pub enum TlvType {
81    /// Filestore Request (type 0x00).
82    FilestoreRequest = 0x00,
83    /// Filestore Response (type 0x01).
84    FilestoreResponse = 0x01,
85    /// Message to User (type 0x02).
86    MessageToUser = 0x02,
87    // 0x03 is unused/reserved
88    /// Fault Handler Override (type 0x04).
89    FaultHandlerOverride = 0x04,
90    /// Flow Label (type 0x05).
91    FlowLabel = 0x05,
92    /// Entity ID (type 0x06).
93    EntityId = 0x06,
94}
95
96impl TryFrom<u8> for TlvType {
97    type Error = CfdpError;
98
99    fn try_from(value: u8) -> Result<Self, Self::Error> {
100        match value {
101            0x00 => Ok(TlvType::FilestoreRequest),
102            0x01 => Ok(TlvType::FilestoreResponse),
103            0x02 => Ok(TlvType::MessageToUser),
104            0x04 => Ok(TlvType::FaultHandlerOverride),
105            0x05 => Ok(TlvType::FlowLabel),
106            0x06 => Ok(TlvType::EntityId),
107            _ => Err(CfdpError::Custom("Unknown TLV type")),
108        }
109    }
110}
111
112/// An iterator that yields zero-copy `Tlv` references from a byte buffer.
113pub struct TlvIterator<'a> {
114    /// The remaining unparsed bytes.
115    pub buffer: &'a [u8],
116}
117
118impl<'a> Iterator for TlvIterator<'a> {
119    type Item = &'a Tlv;
120
121    fn next(&mut self) -> Option<Self::Item> {
122        if self.buffer.len() < 2 {
123            return None;
124        }
125        let length = self.buffer[1] as usize;
126        let total_len = 2 + length;
127        if self.buffer.len() < total_len {
128            self.buffer = &[];
129            return None;
130        }
131        let (tlv_bytes, rest) = self.buffer.split_at(total_len);
132        let tlv = Tlv::ref_from_bytes(tlv_bytes).ok()?;
133        self.buffer = rest;
134
135        Some(tlv)
136    }
137}
138
139/// Action codes for Filestore Request TLV (Table 5-16)
140#[derive(Debug, Copy, Clone, PartialEq, Eq)]
141#[repr(u8)]
142pub enum FilestoreAction {
143    /// Create a new file.
144    CreateFile = 0x00,
145    /// Delete an existing file.
146    DeleteFile = 0x01,
147    /// Rename a file.
148    RenameFile = 0x02,
149    /// Append to an existing file.
150    AppendFile = 0x03,
151    /// Replace an existing file.
152    ReplaceFile = 0x04,
153    /// Create a new directory.
154    CreateDirectory = 0x05,
155    /// Remove a directory.
156    RemoveDirectory = 0x06,
157    /// Deny creation of a file.
158    DenyFile = 0x07,
159    /// Deny creation of a directory.
160    DenyDirectory = 0x08,
161}
162
163impl TryFrom<u8> for FilestoreAction {
164    type Error = CfdpError;
165    fn try_from(value: u8) -> Result<Self, Self::Error> {
166        match value {
167            0x00 => Ok(Self::CreateFile),
168            0x01 => Ok(Self::DeleteFile),
169            0x02 => Ok(Self::RenameFile),
170            0x03 => Ok(Self::AppendFile),
171            0x04 => Ok(Self::ReplaceFile),
172            0x05 => Ok(Self::CreateDirectory),
173            0x06 => Ok(Self::RemoveDirectory),
174            0x07 => Ok(Self::DenyFile),
175            0x08 => Ok(Self::DenyDirectory),
176            _ => Err(CfdpError::Custom("Invalid FilestoreAction code")),
177        }
178    }
179}