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}