leodos_protocols/transport/cfdp/pdu/file_directive/
eof.rs1use crate::transport::cfdp::pdu::CfdpError;
2use crate::transport::cfdp::pdu::EntityId;
3use crate::transport::cfdp::pdu::Pdu;
4use crate::transport::cfdp::pdu::TransactionSeqNum;
5use crate::transport::cfdp::pdu::file_directive::ConditionCode;
6use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
7use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
8use crate::transport::cfdp::pdu::header::Direction;
9use crate::transport::cfdp::pdu::header::PduHeaderFixedPart;
10use crate::transport::cfdp::pdu::header::PduType;
11use crate::transport::cfdp::pdu::header::TransmissionMode;
12use crate::transport::cfdp::pdu::tlv::Tlv;
13use crate::transport::cfdp::pdu::tlv::TlvIterator;
14use crate::transport::cfdp::pdu::tlv::TlvType;
15use crate::utils::get_bits_u8;
16use crate::utils::set_bits_u8;
17
18use bon::bon;
19use zerocopy::FromBytes;
20use zerocopy::Immutable;
21use zerocopy::IntoBytes;
22use zerocopy::KnownLayout;
23use zerocopy::Unaligned;
24use zerocopy::network_endian::U32;
25use zerocopy::network_endian::U64;
26
27#[repr(C)]
51#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
52pub struct EofPdu {
53 condition_code_and_spare: u8,
55 file_checksum: U32,
57 rest: [u8],
59}
60
61mod bitmasks {
63 pub const EOF_CC_MASK: u8 = 0b_11110000;
65}
66
67use bitmasks::*;
68
69impl EofPdu {
70 pub fn condition_code(&self) -> Result<ConditionCode, CfdpError> {
72 ConditionCode::try_from(get_bits_u8(self.condition_code_and_spare, EOF_CC_MASK))
74 }
75 pub fn set_condition_code(&mut self, code: ConditionCode) {
77 set_bits_u8(&mut self.condition_code_and_spare, EOF_CC_MASK, code as u8);
78 }
79
80 pub fn file_checksum(&self) -> u32 {
82 self.file_checksum.get()
83 }
84 pub fn set_file_checksum(&mut self, checksum: u32) {
86 self.file_checksum.set(checksum);
87 }
88
89 pub fn file_size(&self, large_file_flag: bool) -> Result<u64, CfdpError> {
95 if large_file_flag {
96 U64::ref_from_prefix(&self.rest)
97 .map(|(len, _)| len.get())
98 .map_err(|_| CfdpError::Custom("Invalid Eof File Size"))
99 } else {
100 U32::ref_from_prefix(&self.rest)
101 .map(|(len, _)| len.get() as u64)
102 .map_err(|_| CfdpError::Custom("Invalid Eof File Size"))
103 }
104 }
105 pub fn set_file_size(
107 &mut self,
108 large_file_flag: bool,
109 file_size: u64,
110 ) -> Result<(), CfdpError> {
111 if large_file_flag {
112 let size_field = U64::new(file_size);
113 let size_bytes = size_field.as_bytes();
114 self.rest
115 .get_mut(0..8)
116 .ok_or(CfdpError::Custom("Insufficient space for 64-bit file size"))?
117 .copy_from_slice(&size_bytes);
118 Ok(())
119 } else {
120 if file_size > u32::MAX as u64 {
121 return Err(CfdpError::Custom("File size too large for 32-bit field"));
122 }
123 let size_field = U32::new(file_size as u32);
124 let size_bytes = size_field.as_bytes();
125 self.rest
126 .get_mut(0..4)
127 .ok_or(CfdpError::Custom("Insufficient space for 32-bit file size"))?
128 .copy_from_slice(&size_bytes);
129 Ok(())
130 }
131 }
132
133 pub fn tlvs(&self, large_file_flag: bool) -> Result<TlvIterator<'_>, CfdpError> {
135 let file_size_len = if large_file_flag { 8 } else { 4 };
136
137 let tlv_buffer = self
138 .rest
139 .get(file_size_len..)
140 .ok_or_else(|| CfdpError::Custom("Invalid Eof PDU: insufficient data for TLVs"))?;
141
142 Ok(TlvIterator { buffer: tlv_buffer })
143 }
144
145 pub fn fault_location(&self, large_file_flag: bool) -> Result<Option<&Tlv>, CfdpError> {
150 let fault_loc_tlv = self
151 .tlvs(large_file_flag)?
152 .find(|tlv| tlv.tlv_type() == Ok(TlvType::EntityId));
153
154 Ok(fault_loc_tlv)
155 }
156
157 pub fn rest(&self) -> &[u8] {
159 &self.rest
160 }
161}
162
163#[bon]
164impl EofPdu {
165 #[builder]
167 pub fn new<'a>(
168 buffer: &'a mut [u8],
169 source_entity_id: EntityId,
170 dest_entity_id: EntityId,
171 transaction_seq_num: TransactionSeqNum,
172 transmission_mode: TransmissionMode,
173 large_file_flag: bool,
174 crc_flag: bool,
175 condition_code: ConditionCode,
176 file_checksum: u32,
177 file_size: u64,
178 fault_location: Option<&'a [u8]>,
179 ) -> Result<&'a mut Pdu, CfdpError> {
180 let fixed_part_len = size_of::<u8>() + size_of::<U32>();
181 let file_size_len = if large_file_flag { 8 } else { 4 };
182 let fault_loc_len = fault_location.map_or(0, |loc| loc.len());
183
184 let specific_data_len = fixed_part_len + file_size_len + fault_loc_len;
185 let data_field_len = (DirectiveCode::size() + specific_data_len) as u16;
186
187 let header = PduHeaderFixedPart::builder()
188 .version(1)
189 .pdu_type(PduType::FileDirective)
190 .direction(Direction::TowardReceiver)
191 .tx_mode(transmission_mode)
192 .crc_flag(crc_flag)
193 .large_file_flag(large_file_flag)
194 .data_field_len(data_field_len)
195 .seg_ctrl(false)
196 .seg_meta_flag(false)
197 .build()?;
198
199 let pdu = Pdu::builder()
200 .buffer(buffer)
201 .header_fixed(header)
202 .source_entity_id(source_entity_id)
203 .destination_entity_id(dest_entity_id)
204 .transaction_seq_num(transaction_seq_num)
205 .build()?;
206
207 let data_field = pdu
208 .data_field_mut()
209 .or_else(|_| Err(CfdpError::Custom("Failed to get data field")))?;
210 let actual_data_field_len = data_field.len();
211 let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field).map_err(|_| {
212 CfdpError::BufferTooSmall {
213 required: data_field_len as usize,
214 provided: actual_data_field_len,
215 }
216 })?;
217 directive_pdu.set_directive_code(DirectiveCode::Eof);
218
219 let remaining_len = directive_pdu.rest.len();
220 let rest_len = file_size_len + fault_loc_len;
221 let eof_pdu = EofPdu::mut_from_bytes_with_elems(&mut directive_pdu.rest, rest_len)
222 .map_err(|_| CfdpError::BufferTooSmall {
223 required: specific_data_len,
224 provided: remaining_len,
225 })?;
226 eof_pdu.set_condition_code(condition_code);
227 eof_pdu.set_file_checksum(file_checksum);
228 eof_pdu
229 .set_file_size(large_file_flag, file_size)
230 .map_err(|_| CfdpError::Custom("Failed to set file size"))?;
231
232 if let Some(location) = fault_location {
234 let tlv_slice = &mut eof_pdu.rest[file_size_len..];
235 let tlv_slice_len = tlv_slice.len();
236 let (tlv, _rest) =
237 Tlv::mut_from_prefix(tlv_slice).map_err(|_| CfdpError::BufferTooSmall {
238 required: fault_loc_len,
239 provided: tlv_slice_len,
240 })?;
241 tlv.set_type(TlvType::EntityId);
242 tlv.set_length(location.len())
243 .map_err(|_| CfdpError::Custom("Failed to set TLV length"))?;
244 tlv.set_value(location)
245 .map_err(|_| CfdpError::Custom("Failed to set TLV value"))?;
246 }
247
248 Ok(pdu)
249 }
250}