Skip to main content

copybook_rdw/
record.rs

1use crate::{RDW_HEADER_LEN, RdwHeader};
2use copybook_error::Result;
3
4/// An RDW record with header and payload bytes.
5#[derive(Debug, Clone)]
6pub struct RDWRecord {
7    /// 4-byte RDW header (length + reserved).
8    pub header: [u8; RDW_HEADER_LEN],
9    /// Record payload bytes.
10    pub payload: Vec<u8>,
11}
12
13impl RDWRecord {
14    /// Create a new RDW record from payload (fallible constructor).
15    ///
16    /// Constructs an [`RDWRecord`] with a computed header (payload length + zero reserved bytes).
17    ///
18    /// # Examples
19    ///
20    /// ```
21    /// use copybook_rdw::RDWRecord;
22    ///
23    /// let record = RDWRecord::try_new(vec![0xC8; 80]).unwrap();
24    /// assert_eq!(record.length(), 80);
25    /// assert_eq!(record.payload.len(), 80);
26    /// ```
27    ///
28    /// # Errors
29    /// Returns an error when payload length exceeds `u16::MAX`.
30    #[inline]
31    #[must_use = "Handle the Result or propagate the error"]
32    pub fn try_new(payload: Vec<u8>) -> Result<Self> {
33        let header = RdwHeader::from_payload_len(payload.len(), 0)?.bytes();
34        Ok(Self { header, payload })
35    }
36
37    /// Create a new RDW record from payload.
38    ///
39    /// # Panics
40    /// Panics when payload length exceeds `u16::MAX`.
41    #[deprecated(
42        since = "0.4.3",
43        note = "use try_new() instead for fallible construction"
44    )]
45    #[allow(clippy::expect_used)] // Intentional panic for deprecated API
46    #[inline]
47    #[must_use]
48    pub fn new(payload: Vec<u8>) -> Self {
49        Self::try_new(payload).expect("RDW payload exceeds maximum size (65535 bytes)")
50    }
51
52    /// Create an RDW record preserving reserved bytes (fallible constructor).
53    ///
54    /// # Errors
55    /// Returns an error when payload length exceeds `u16::MAX`.
56    #[inline]
57    #[must_use = "Handle the Result or propagate the error"]
58    pub fn try_with_reserved(payload: Vec<u8>, reserved: u16) -> Result<Self> {
59        let header = RdwHeader::from_payload_len(payload.len(), reserved)?.bytes();
60        Ok(Self { header, payload })
61    }
62
63    /// Create an RDW record preserving reserved bytes.
64    ///
65    /// # Panics
66    /// Panics when payload length exceeds `u16::MAX`.
67    #[deprecated(
68        since = "0.4.3",
69        note = "use try_with_reserved() instead for fallible construction"
70    )]
71    #[allow(clippy::expect_used)] // Intentional panic for deprecated API
72    #[inline]
73    #[must_use]
74    pub fn with_reserved(payload: Vec<u8>, reserved: u16) -> Self {
75        Self::try_with_reserved(payload, reserved)
76            .expect("RDW payload exceeds maximum size (65535 bytes)")
77    }
78
79    /// Get payload length from header.
80    #[inline]
81    #[must_use]
82    pub fn length(&self) -> u16 {
83        RdwHeader::from_bytes(self.header).length()
84    }
85
86    /// Get reserved bytes from header.
87    #[inline]
88    #[must_use]
89    pub fn reserved(&self) -> u16 {
90        RdwHeader::from_bytes(self.header).reserved()
91    }
92
93    /// Recompute the header length field from payload length.
94    ///
95    /// # Errors
96    /// Returns an error when payload length exceeds `u16::MAX`.
97    #[inline]
98    #[must_use = "Handle the Result or propagate the error"]
99    pub fn try_recompute_length(&mut self) -> Result<()> {
100        self.header = RdwHeader::from_payload_len(self.payload.len(), self.reserved())?.bytes();
101        Ok(())
102    }
103
104    /// Recompute the header length field from payload length.
105    ///
106    /// # Panics
107    /// Panics when payload length exceeds `u16::MAX`.
108    #[deprecated(
109        since = "0.4.3",
110        note = "use try_recompute_length() instead for fallible operation"
111    )]
112    #[allow(clippy::expect_used)] // Intentional panic for deprecated API
113    #[inline]
114    pub fn recompute_length(&mut self) {
115        self.try_recompute_length()
116            .expect("RDW payload exceeds maximum size (65535 bytes)");
117    }
118
119    /// Serialize record as `header + payload`.
120    #[inline]
121    #[must_use]
122    pub fn as_bytes(&self) -> Vec<u8> {
123        let mut result = Vec::with_capacity(RDW_HEADER_LEN + self.payload.len());
124        result.extend_from_slice(&self.header);
125        result.extend_from_slice(&self.payload);
126        result
127    }
128}