Skip to main content

smpp_codec/pdus/session_pdus/
unbind.rs

1use crate::common::{
2    get_status_code, get_status_description, PduError, CMD_UNBIND, CMD_UNBIND_RESP, HEADER_LEN,
3};
4use std::io::{Cursor, Read, Write};
5
6// --- Unbind Request ---
7/// Represents an Unbind Request PDU.
8///
9/// Used to request the termination of a session.
10#[derive(Debug, Clone, PartialEq)]
11pub struct UnbindRequest {
12    /// Sequence number of the PDU
13    pub sequence_number: u32,
14}
15
16impl UnbindRequest {
17    /// Create a new Unbind Request.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use smpp_codec::pdus::UnbindRequest;
23    ///
24    /// let sequence_number: u32 = 1;
25    /// let unbind_req = UnbindRequest::new(sequence_number);
26    /// ```
27    pub fn new(sequence_number: u32) -> Self {
28        Self { sequence_number }
29    }
30
31    /// Encode the struct into raw bytes for the network.
32    ///
33    /// # Errors
34    ///
35    /// Returns a [`PduError`] if an I/O error occurs while writing.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// # use smpp_codec::pdus::UnbindRequest;
41    /// # let sequence_number: u32 = 1;
42    /// # let unbind_req = UnbindRequest::new(sequence_number);
43    /// let mut buffer = Vec::new();
44    /// unbind_req.encode(&mut buffer).expect("Encoding failed");
45    /// ```
46    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
47        let command_len = HEADER_LEN as u32;
48        writer.write_all(&command_len.to_be_bytes())?;
49        writer.write_all(&CMD_UNBIND.to_be_bytes())?;
50        writer.write_all(&0u32.to_be_bytes())?; // Status always 0 for requests
51        writer.write_all(&self.sequence_number.to_be_bytes())?;
52        Ok(())
53    }
54
55    /// Decode raw bytes from the network into the struct.
56    ///
57    /// # Errors
58    ///
59    /// Returns a [`PduError`] if the buffer is too short.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// # use smpp_codec::pdus::UnbindRequest;
65    /// # let sequence_number: u32 = 1;
66    /// # let unbind_req = UnbindRequest::new(sequence_number);
67    /// # let mut buffer = Vec::new();
68    /// # unbind_req.encode(&mut buffer).unwrap();
69    /// let decoded = UnbindRequest::decode(&buffer).expect("Decoding failed");
70    /// assert_eq!(decoded.sequence_number, 1);
71    /// ```
72    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
73        if buffer.len() < HEADER_LEN {
74            return Err(PduError::BufferTooShort);
75        }
76        let mut cursor = Cursor::new(buffer);
77        cursor.set_position(12); // Skip len, id, status
78
79        let mut bytes = [0u8; 4];
80        cursor.read_exact(&mut bytes)?;
81        let sequence_number = u32::from_be_bytes(bytes);
82
83        Ok(Self { sequence_number })
84    }
85}
86
87// --- Unbind Response ---
88/// Represents an Unbind Response PDU.
89///
90/// Sent by the SMSC in response to an Unbind Request.
91#[derive(Debug, Clone, PartialEq)]
92pub struct UnbindResponse {
93    /// Sequence number of the PDU
94    pub sequence_number: u32,
95    /// Command Status (0 = OK, others = Error)
96    pub command_status: u32,
97    /// Human-readable description of status
98    pub status_description: String,
99}
100
101impl UnbindResponse {
102    /// Create a new Unbind Response.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// use smpp_codec::pdus::UnbindResponse;
108    ///
109    /// let sequence_number: u32 = 1;
110    /// let unbind_resp = UnbindResponse::new(sequence_number, "ESME_ROK");
111    /// ```
112    pub fn new(sequence_number: u32, status_name: &str) -> Self {
113        let command_status = get_status_code(status_name);
114        Self {
115            sequence_number,
116            command_status,
117            status_description: status_name.to_string(),
118        }
119    }
120
121    /// Encode the struct into raw bytes for the network.
122    ///
123    /// # Errors
124    ///
125    /// Returns a [`PduError`] if an I/O error occurs while writing.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// # use smpp_codec::pdus::UnbindResponse;
131    /// # let sequence_number: u32 = 1;
132    /// # let unbind_resp = UnbindResponse::new(sequence_number, "ESME_ROK");
133    /// let mut buffer = Vec::new();
134    /// unbind_resp.encode(&mut buffer).expect("Encoding failed");
135    /// ```
136    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
137        let command_len = HEADER_LEN as u32;
138        writer.write_all(&command_len.to_be_bytes())?;
139        writer.write_all(&CMD_UNBIND_RESP.to_be_bytes())?;
140        writer.write_all(&self.command_status.to_be_bytes())?;
141        writer.write_all(&self.sequence_number.to_be_bytes())?;
142        Ok(())
143    }
144
145    /// Decode raw bytes from the network into the struct.
146    ///
147    /// # Errors
148    ///
149    /// Returns a [`PduError`] if the buffer is too short.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// # use smpp_codec::pdus::UnbindResponse;
155    /// # let sequence_number: u32 = 1;
156    /// # let unbind_resp = UnbindResponse::new(sequence_number, "ESME_ROK");
157    /// # let mut buffer = Vec::new();
158    /// # unbind_resp.encode(&mut buffer).unwrap();
159    /// let decoded = UnbindResponse::decode(&buffer).expect("Decoding failed");
160    /// assert_eq!(decoded.sequence_number, 1);
161    /// ```
162    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
163        if buffer.len() < HEADER_LEN {
164            return Err(PduError::BufferTooShort);
165        }
166        let mut cursor = Cursor::new(buffer);
167
168        // Skip Length (4) + ID (4)
169        cursor.set_position(8);
170
171        let mut bytes = [0u8; 4];
172
173        // Read Status
174        cursor.read_exact(&mut bytes)?;
175        let command_status = u32::from_be_bytes(bytes);
176
177        // Read Sequence
178        cursor.read_exact(&mut bytes)?;
179        let sequence_number = u32::from_be_bytes(bytes);
180
181        Ok(Self {
182            sequence_number,
183            command_status,
184            status_description: get_status_description(command_status),
185        })
186    }
187}