Skip to main content

smpp_codec/pdus/session_pdus/
bind_response.rs

1// bind_response.rs (Handles both Encoder and Decoder)
2use crate::common::{get_status_code, get_status_description, read_c_string, PduError, HEADER_LEN};
3use std::io::{Cursor, Read, Write};
4
5/// Represents a Bind Response PDU.
6///
7/// Sent by the SMSC in response to a Bind Request.
8#[derive(Debug, Clone, PartialEq)]
9pub struct BindResponse {
10    /// Sequence number of the PDU
11    pub sequence_number: u32,
12    /// Command Status (0 = OK, others = Error)
13    pub command_status: u32, // 0 = OK, others = Error
14    /// Human-readable description of status
15    pub status_description: String, // Human-readable description of status
16    /// Command ID (e.g., 0x80000009 for bind_transceiver_resp)
17    pub command_id: u32, // e.g., 0x80000009 (bind_transceiver_resp)
18    /// System ID identifying the SMSC
19    pub system_id: String, // SMSC ID
20    /// Optional TLV parameters
21    pub optional_params: Vec<u8>, // Rarely used in Bind Resp, but allowed
22}
23
24impl BindResponse {
25    /// Create a new Bind Response.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use smpp_codec::pdus::BindResponse;
31    /// use smpp_codec::common::{CMD_BIND_TRANSCEIVER_RESP, BindMode};
32    ///
33    /// let sequence_number: u32 = 1;
34    /// let resp = BindResponse::new(
35    ///     sequence_number,
36    ///     CMD_BIND_TRANSCEIVER_RESP,
37    ///     "ESME_ROK",
38    ///     "system_id".to_string()
39    /// );
40    /// ```
41    pub fn new(
42        sequence_number: u32,
43        command_id: u32,
44        status_name: &str,
45        system_id: String,
46    ) -> Self {
47        let command_status = get_status_code(status_name);
48        Self {
49            sequence_number,
50            command_id,
51            command_status,
52            status_description: status_name.to_string(),
53            system_id,
54            optional_params: Vec::new(),
55        }
56    }
57
58    /// Encode the struct into raw bytes for the network.
59    ///
60    /// # Errors
61    ///
62    /// Returns a [`PduError`] if:
63    /// * An I/O error occurs while writing.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// # use smpp_codec::pdus::BindResponse;
69    /// # use smpp_codec::common::CMD_BIND_TRANSMITTER_RESP;
70    /// # let sequence_number: u32 = 1;
71    /// # let bind_resp = BindResponse::new(sequence_number, CMD_BIND_TRANSMITTER_RESP, "ESME_ROK", "id".into());
72    /// let mut buffer = Vec::new();
73    /// bind_resp.encode(&mut buffer).expect("Encoding failed");
74    /// ```
75    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
76        let mut body = Vec::new();
77
78        // If status is OK, we must include system_id.
79        // On error, body can be empty (or just header).
80        if self.command_status == 0 {
81            body.write_all(self.system_id.as_bytes())?;
82            body.write_all(&[0])?; // Null terminator
83
84            // Add optional params if any
85            body.write_all(&self.optional_params)?;
86        }
87
88        let command_len = (HEADER_LEN + body.len()) as u32;
89
90        // Write Header
91        writer.write_all(&command_len.to_be_bytes())?;
92        writer.write_all(&self.command_id.to_be_bytes())?;
93        writer.write_all(&self.command_status.to_be_bytes())?;
94        writer.write_all(&self.sequence_number.to_be_bytes())?;
95
96        // Write Body
97        writer.write_all(&body)?;
98
99        Ok(())
100    }
101
102    /// Decode raw bytes from the network into the struct.
103    ///
104    /// # Errors
105    ///
106    /// Returns a [`PduError`] if:
107    /// * The buffer is too short.
108    /// * The buffer data is malformed.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// # use smpp_codec::pdus::BindResponse;
114    /// # use smpp_codec::common::CMD_BIND_TRANSCEIVER_RESP;
115    /// # let sequence_number: u32 = 1;
116    /// # let resp = BindResponse::new(sequence_number, CMD_BIND_TRANSCEIVER_RESP, "ESME_ROK", "id".into());
117    /// # let mut buffer = Vec::new();
118    /// # resp.encode(&mut buffer).unwrap();
119    /// let decoded = BindResponse::decode(&buffer).expect("Decoding failed");
120    /// assert_eq!(decoded.sequence_number, 1);
121    /// ```
122    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
123        if buffer.len() < HEADER_LEN {
124            return Err(PduError::BufferTooShort);
125        }
126
127        let mut cursor = Cursor::new(buffer);
128
129        // 1. Read Header
130        let mut bytes = [0u8; 4];
131
132        cursor.read_exact(&mut bytes)?;
133        let command_len = u32::from_be_bytes(bytes) as usize;
134
135        cursor.read_exact(&mut bytes)?;
136        let command_id = u32::from_be_bytes(bytes);
137
138        cursor.read_exact(&mut bytes)?;
139        let command_status = u32::from_be_bytes(bytes);
140
141        cursor.read_exact(&mut bytes)?;
142        let sequence_number = u32::from_be_bytes(bytes);
143
144        // 2. Read Body
145        let system_id: String;
146        let mut optional_params = Vec::new();
147
148        // Only try to read body if length > header
149        if command_len > HEADER_LEN {
150            // The cursor is currently at pos 16 (end of header).
151            // Read system_id (C-String)
152            if command_status == 0 {
153                system_id = read_c_string(&mut cursor)?;
154            } else {
155                system_id = String::new();
156            }
157        } else {
158            system_id = String::new();
159        }
160
161        // Optional Params
162        // optional_params reused from above
163        let current_pos = cursor.position() as usize;
164        if current_pos < command_len {
165            let remaining_len = command_len - current_pos;
166            let mut temp_buf = vec![0u8; remaining_len];
167            cursor.read_exact(&mut temp_buf)?;
168            optional_params = temp_buf;
169        }
170
171        let status_description = get_status_description(command_status);
172
173        Ok(Self {
174            sequence_number,
175            command_status,
176            status_description,
177            command_id,
178            system_id,
179            optional_params,
180        })
181    }
182}