Skip to main content

leodos_protocols/transport/cfdp/pdu/file_directive/nak/
small.rs

1use crate::transport::cfdp::CfdpError;
2use crate::transport::cfdp::pdu::EntityId;
3use crate::transport::cfdp::pdu::Pdu;
4use crate::transport::cfdp::pdu::PduHeaderFixedPart;
5use crate::transport::cfdp::pdu::TransactionSeqNum;
6use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
7use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
8use crate::transport::cfdp::pdu::file_directive::nak::NakSegmentRequest;
9use crate::transport::cfdp::pdu::header::Direction;
10use crate::transport::cfdp::pdu::header::PduType;
11use crate::transport::cfdp::pdu::header::TransmissionMode;
12
13use bon::bon;
14use zerocopy::FromBytes;
15use zerocopy::Immutable;
16use zerocopy::IntoBytes;
17use zerocopy::KnownLayout;
18use zerocopy::Unaligned;
19use zerocopy::network_endian::U32;
20
21/// A zero-copy representation of the data field of a NAK PDU for **small files**.
22///
23/// ```text
24/// +------------------------------------+-----------+--------------------------------------+
25/// | Field Name                         | Size      | Notes                                |
26/// +------------------------------------+-----------+--------------------------------------+
27/// | Start of scope                     | 32 bits   | FSS field.                           |
28/// | End of scope                       | 32 bits   | FSS field.                           |
29/// | -- Start of `rest` slice --------- | --------- | ------------------------------------ |
30/// | Segment Requests                   | Variable  | Zero or more `NakSegmentSmall`s.     |
31/// +------------------------------------+-----------+--------------------------------------+
32/// ```
33#[repr(C)]
34#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
35pub struct NakPduSmall {
36    /// The 32-bit start of scope offset.
37    start_of_scope: U32,
38    /// The 32-bit end of scope offset.
39    end_of_scope: U32,
40    /// Contains zero or more `NakSegmentSmall` segment requests.
41    rest: [u8],
42}
43
44impl NakPduSmall {
45    /// Get the start_of_scope field as a u32.
46    pub fn start_of_scope(&self) -> u32 {
47        self.start_of_scope.get()
48    }
49    /// Sets the start of scope field.
50    pub fn set_start_of_scope(&mut self, scope: u32) {
51        self.start_of_scope.set(scope);
52    }
53
54    /// Get the end_of_scope field as a u32.
55    pub fn end_of_scope(&self) -> u32 {
56        self.end_of_scope.get()
57    }
58    /// Sets the end of scope field.
59    pub fn set_end_of_scope(&mut self, scope: u32) {
60        self.end_of_scope.set(scope);
61    }
62
63    /// Get the raw rest field as a byte slice.
64    pub fn rest(&self) -> &[u8] {
65        &self.rest
66    }
67
68    /// Parses the `rest` slice into a slice of `NakSegmentSmall`s.
69    pub fn segment_requests(&self) -> Result<&[NakSegmentSmall], CfdpError> {
70        <[NakSegmentSmall]>::ref_from_bytes(&self.rest)
71            .map_err(|_| CfdpError::Custom("Invalid NAK segment requests"))
72    }
73}
74
75/// A `zerocopy`-compatible struct representing a single missing segment in a NAK PDU
76/// for a **small file** transaction (32-bit offsets).
77#[repr(C)]
78#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
79pub struct NakSegmentSmall {
80    /// Start offset of the missing data segment.
81    start_offset: U32,
82    /// End offset of the missing data segment.
83    end_offset: U32,
84}
85
86impl NakSegmentSmall {
87    /// Get the start_offset field as a u32.
88    pub fn start_offset(&self) -> u32 {
89        self.start_offset.get()
90    }
91    /// Sets the start offset of the missing segment.
92    pub fn set_start_offset(&mut self, offset: u32) {
93        self.start_offset.set(offset);
94    }
95
96    /// Get the end_offset field as a u32.
97    pub fn end_offset(&self) -> u32 {
98        self.end_offset.get()
99    }
100    /// Sets the end offset of the missing segment.
101    pub fn set_end_offset(&mut self, offset: u32) {
102        self.end_offset.set(offset);
103    }
104}
105
106#[bon]
107impl NakPduSmall {
108    /// Builds a new small-file NAK PDU in the given buffer.
109    #[builder]
110    pub fn new<'a>(
111        buffer: &'a mut [u8],
112        source_entity_id: EntityId,
113        dest_entity_id: EntityId,
114        transaction_seq_num: TransactionSeqNum,
115        transmission_mode: TransmissionMode,
116        crc_flag: bool,
117        start_of_scope: u32,
118        end_of_scope: u32,
119        segment_requests: &'a [NakSegmentRequest],
120    ) -> Result<&'a mut Pdu, CfdpError> {
121        let fixed_part_len = size_of::<U32>() * 2;
122        let segments_len = segment_requests.len() * size_of::<NakSegmentSmall>();
123        let specific_data_len = fixed_part_len + segments_len;
124        let data_field_len = (1 + specific_data_len) as u16;
125
126        let header = PduHeaderFixedPart::builder()
127            .version(1)
128            .pdu_type(PduType::FileDirective)
129            .direction(Direction::TowardSender)
130            .tx_mode(transmission_mode)
131            .crc_flag(crc_flag)
132            .large_file_flag(false) // This is the small version
133            .data_field_len(data_field_len)
134            .seg_ctrl(false)
135            .seg_meta_flag(false)
136            .build()?;
137
138        let pdu = Pdu::builder()
139            .buffer(buffer)
140            .header_fixed(header)
141            .source_entity_id(source_entity_id)
142            .destination_entity_id(dest_entity_id)
143            .transaction_seq_num(transaction_seq_num)
144            .build()?;
145
146        let data_field = pdu.data_field_mut().or_else(|_| {
147            Err(CfdpError::Custom(
148                "Failed to get mutable data field for NAK PDU",
149            ))
150        })?;
151        let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field).or_else(|_| {
152            Err(CfdpError::Custom(
153                "Failed to get mutable directive PDU for NAK PDU",
154            ))
155        })?;
156        directive_pdu.set_directive_code(DirectiveCode::Nak);
157
158        let nak_pdu =
159            NakPduSmall::mut_from_bytes_with_elems(&mut directive_pdu.rest, segments_len)
160                .or_else(|_| Err(CfdpError::Custom("Failed to build NAK PDU")))?;
161        nak_pdu.set_start_of_scope(start_of_scope);
162        nak_pdu.set_end_of_scope(end_of_scope);
163
164        let segments_slice =
165            <[NakSegmentSmall]>::mut_from_bytes(&mut nak_pdu.rest).or_else(|_| {
166                Err(CfdpError::Custom(
167                    "Failed to get mutable segment requests slice for NAK PDU",
168                ))
169            })?;
170        for (req, seg) in segment_requests.iter().zip(segments_slice.iter_mut()) {
171            if req.start_offset > u32::MAX as u64 || req.end_offset > u32::MAX as u64 {
172                return Err(CfdpError::DataTooLarge {
173                    field: "segment offset",
174                    max: u32::MAX as usize,
175                });
176            }
177            seg.set_start_offset(req.start_offset as u32);
178            seg.set_end_offset(req.end_offset as u32);
179        }
180
181        Ok(pdu)
182    }
183}