Skip to main content

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

1use zerocopy::FromBytes;
2use zerocopy::Immutable;
3use zerocopy::IntoBytes;
4use zerocopy::KnownLayout;
5use zerocopy::Unaligned;
6
7use crate::transport::cfdp::CfdpError;
8
9/// Acknowledgment (ACK) PDU.
10pub mod ack;
11/// End-of-File (EOF) PDU.
12pub mod eof;
13/// Finished PDU.
14pub mod finished;
15/// Keep Alive PDU (small and large file variants).
16pub mod keepalive;
17/// Metadata PDU.
18pub mod metadata;
19/// Negative Acknowledgment (NAK) PDU (small and large file variants).
20pub mod nak;
21/// Prompt PDU.
22pub mod prompt;
23
24/// A zero-copy representation of the start of any File Directive's data field.
25/// It provides the directive code, which can then be used to parse the `rest`
26/// of the data field into a more specific PDU type (EofPdu, FinishedPdu, etc.).
27///
28/// ```text
29/// +------------------------------------+----------------+
30/// | Field Name                         | Size           |
31/// +------------------------------------+----------------+
32/// | Directive Code                     | 8 bits         |
33/// | `rest` (contents depend on code)   | Variable       |
34/// +------------------------------------+----------------+
35/// ```
36#[repr(C)]
37#[derive(FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
38pub struct FileDirectivePdu {
39    /// The raw directive code byte.
40    directive_code: u8,
41    /// The directive-specific data following the code byte.
42    rest: [u8],
43}
44
45/// Identifies the type of File Directive PDU (Table 5-4).
46#[repr(u8)]
47#[derive(Debug, PartialEq, Eq, Clone, Copy)]
48pub enum DirectiveCode {
49    /// End-of-File directive.
50    Eof = 0x04,
51    /// Finished directive.
52    Finished = 0x05,
53    /// Acknowledgment directive.
54    Ack = 0x06,
55    /// Metadata directive.
56    Metadata = 0x07,
57    /// Negative Acknowledgment directive.
58    Nak = 0x08,
59    /// Prompt directive.
60    Prompt = 0x09,
61    /// Keep Alive directive.
62    KeepAlive = 0x0C,
63}
64
65impl DirectiveCode {
66    /// Returns the size in bytes of the DirectiveCode field.
67    pub fn size() -> usize {
68        1
69    }
70}
71
72impl TryFrom<u8> for DirectiveCode {
73    type Error = CfdpError;
74    fn try_from(val: u8) -> Result<Self, Self::Error> {
75        let val = match val {
76            0x04 => DirectiveCode::Eof,
77            0x05 => DirectiveCode::Finished,
78            0x06 => DirectiveCode::Ack,
79            0x07 => DirectiveCode::Metadata,
80            0x08 => DirectiveCode::Nak,
81            0x09 => DirectiveCode::Prompt,
82            0x0C => DirectiveCode::KeepAlive,
83            _ => return Err(CfdpError::Custom("Invalid DirectiveCode value")),
84        };
85        Ok(val)
86    }
87}
88
89impl FileDirectivePdu {
90    /// Returns the DirectiveCode enum variant for this PDU.
91    pub fn directive_code(&self) -> Result<DirectiveCode, CfdpError> {
92        self.directive_code.try_into()
93    }
94
95    /// Sets the directive code for this PDU.
96    pub fn set_directive_code(&mut self, code: DirectiveCode) {
97        self.directive_code = code as u8;
98    }
99
100    /// Get the raw rest field as a byte slice.
101    pub fn rest(&self) -> &[u8] {
102        &self.rest
103    }
104}
105
106/// Represents the Condition Code reported in `EOF`, `Finished`, and `ACK` PDUs.
107#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
108#[repr(u8)]
109pub enum ConditionCode {
110    /// No error was detected.
111    #[default]
112    NoError = 0,
113    /// The acknowledgment limit was reached without receiving an expected ACK.
114    AckLimitReached = 1,
115    /// The keep-alive limit was reached without receiving any PDU for the transaction.
116    KeepAliveLimitReached = 2,
117    /// An invalid transmission mode was specified.
118    InvalidTransmissionMode = 3,
119    /// The filestore operation was rejected by the receiver.
120    FilestoreRejection = 4,
121    /// The file checksum did not match the expected value.
122    FileChecksumFailure = 5,
123    /// The file size did not match the expected value.
124    FileSizeError = 6,
125    /// The NAK limit was reached without satisfying all missing data requests.
126    NakLimitReached = 7,
127    /// An inactivity timer expired.
128    InactivityDetected = 8,
129    /// Invalid file structure.
130    InvalidFileStructure = 9,
131    /// The check limit was reached.
132    CheckLimitReached = 10,
133    /// The requested checksum type is not supported.
134    UnsupportedChecksumType = 11,
135    /// A `SUSPEND` request was received for the transaction.
136    SuspendReceived = 14,
137    /// A `CANCEL` request was received for the transaction.
138    CancelReceived = 15,
139}
140
141impl TryFrom<u8> for ConditionCode {
142    type Error = CfdpError;
143    fn try_from(val: u8) -> Result<Self, Self::Error> {
144        let val = match val {
145            0 => ConditionCode::NoError,
146            1 => ConditionCode::AckLimitReached,
147            2 => ConditionCode::KeepAliveLimitReached,
148            3 => ConditionCode::InvalidTransmissionMode,
149            4 => ConditionCode::FilestoreRejection,
150            5 => ConditionCode::FileChecksumFailure,
151            6 => ConditionCode::FileSizeError,
152            7 => ConditionCode::NakLimitReached,
153            8 => ConditionCode::InactivityDetected,
154            9 => ConditionCode::InvalidFileStructure,
155            10 => ConditionCode::CheckLimitReached,
156            11 => ConditionCode::UnsupportedChecksumType,
157            14 => ConditionCode::SuspendReceived,
158            15 => ConditionCode::CancelReceived,
159            _ => return Err(CfdpError::Custom("Invalid ConditionCode value")),
160        };
161        Ok(val)
162    }
163}