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