rusmpp_core/session/
session_state.rs1use crate::CommandId;
2
3#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
10pub enum SessionState {
11 #[default]
16 Closed,
17
18 Open,
24
25 BoundTx,
30
31 BoundRx,
36
37 BoundTrx,
42
43 Outbound,
49
50 Unbound,
56}
57
58impl SessionState {
59 pub const fn is_bound(self) -> bool {
64 matches!(self, Self::BoundTx | Self::BoundRx | Self::BoundTrx)
65 }
66
67 pub const fn can_send_as_esme(self, command: CommandId) -> bool {
77 match self {
78 SessionState::Closed => false,
79 SessionState::Open | SessionState::Outbound => {
80 matches!(
81 command,
82 CommandId::BindReceiver
83 | CommandId::BindTransmitter
84 | CommandId::BindTransceiver
85 | CommandId::EnquireLink
86 | CommandId::EnquireLinkResp
87 | CommandId::GenericNack
88 )
89 }
90 SessionState::BoundTx => {
91 matches!(
92 command,
93 CommandId::BroadcastSm
94 | CommandId::CancelBroadcastSm
95 | CommandId::CancelSm
96 | CommandId::DataSm
97 | CommandId::EnquireLink
98 | CommandId::EnquireLinkResp
99 | CommandId::GenericNack
100 | CommandId::QueryBroadcastSm
101 | CommandId::QuerySm
102 | CommandId::ReplaceSm
103 | CommandId::SubmitMulti
104 | CommandId::SubmitSm
105 | CommandId::Unbind
106 | CommandId::UnbindResp
107 )
108 }
109 SessionState::BoundRx => {
110 matches!(
111 command,
112 CommandId::DataSmResp
113 | CommandId::DeliverSmResp
114 | CommandId::EnquireLink
115 | CommandId::EnquireLinkResp
116 | CommandId::GenericNack
117 | CommandId::Unbind
118 | CommandId::UnbindResp
119 )
120 }
121 SessionState::BoundTrx => {
122 SessionState::BoundTx.can_send_as_esme(command)
123 || SessionState::BoundRx.can_send_as_esme(command)
124 }
125 SessionState::Unbound => {
126 matches!(
127 command,
128 CommandId::EnquireLink | CommandId::EnquireLinkResp | CommandId::GenericNack
129 )
130 }
131 }
132 }
133
134 pub const fn can_receive_as_esme(self, command: CommandId) -> bool {
144 self.can_send_as_mc(command)
145 }
146
147 pub const fn can_send_as_mc(self, command: CommandId) -> bool {
157 match self {
158 SessionState::Closed => false,
159 SessionState::Open => {
160 matches!(
161 command,
162 CommandId::BindReceiverResp
163 | CommandId::BindTransmitterResp
164 | CommandId::BindTransceiverResp
165 | CommandId::EnquireLink
166 | CommandId::EnquireLinkResp
167 | CommandId::GenericNack
168 | CommandId::Outbind
169 )
170 }
171 SessionState::Outbound => {
172 matches!(
173 command,
174 CommandId::BindReceiverResp
175 | CommandId::BindTransmitterResp
176 | CommandId::BindTransceiverResp
177 | CommandId::EnquireLink
178 | CommandId::EnquireLinkResp
179 | CommandId::GenericNack
180 )
181 }
182 SessionState::BoundTx => {
183 matches!(
184 command,
185 CommandId::BroadcastSmResp
186 | CommandId::CancelBroadcastSmResp
187 | CommandId::CancelSmResp
188 | CommandId::DataSmResp
189 | CommandId::EnquireLink
190 | CommandId::EnquireLinkResp
191 | CommandId::GenericNack
192 | CommandId::QueryBroadcastSmResp
193 | CommandId::QuerySmResp
194 | CommandId::ReplaceSmResp
195 | CommandId::SubmitMultiResp
196 | CommandId::SubmitSmResp
197 | CommandId::Unbind
198 | CommandId::UnbindResp
199 )
200 }
201 SessionState::BoundRx => {
202 matches!(
203 command,
204 CommandId::AlertNotification
205 | CommandId::DataSm
206 | CommandId::DeliverSm
207 | CommandId::EnquireLink
208 | CommandId::EnquireLinkResp
209 | CommandId::GenericNack
210 | CommandId::Unbind
211 | CommandId::UnbindResp
212 )
213 }
214 SessionState::BoundTrx => {
215 SessionState::BoundTx.can_send_as_mc(command)
216 || SessionState::BoundRx.can_send_as_mc(command)
217 }
218 SessionState::Unbound => {
219 matches!(
220 command,
221 CommandId::EnquireLink | CommandId::EnquireLinkResp | CommandId::GenericNack
222 )
223 }
224 }
225 }
226
227 pub const fn can_receive_as_mc(self, command: CommandId) -> bool {
237 self.can_send_as_esme(command)
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use strum::IntoEnumIterator;
245
246 const ESME_BOUND_TX_COMMANDS: [CommandId; 9] = [
247 CommandId::BroadcastSm,
248 CommandId::CancelBroadcastSm,
249 CommandId::CancelSm,
250 CommandId::DataSm,
251 CommandId::QueryBroadcastSm,
252 CommandId::QuerySm,
253 CommandId::ReplaceSm,
254 CommandId::SubmitMulti,
255 CommandId::SubmitSm,
256 ];
257
258 const ESME_BOUND_RX_COMMANDS: [CommandId; 2] =
259 [CommandId::DataSmResp, CommandId::DeliverSmResp];
260
261 #[test]
262 fn test_is_bound() {
263 assert!(!SessionState::Closed.is_bound());
264 assert!(SessionState::BoundTx.is_bound());
265 assert!(SessionState::BoundRx.is_bound());
266 assert!(SessionState::BoundTrx.is_bound());
267 assert!(!SessionState::Open.is_bound());
268 assert!(!SessionState::Outbound.is_bound());
269 assert!(!SessionState::Unbound.is_bound());
270 }
271
272 #[test]
273 fn test_status_close() {
274 for command in CommandId::iter() {
275 assert!(!SessionState::Closed.can_send_as_esme(command));
276 assert!(!SessionState::Closed.can_send_as_mc(command));
277 assert!(!SessionState::Closed.can_receive_as_esme(command));
278 assert!(!SessionState::Closed.can_receive_as_mc(command));
279 }
280 }
281
282 #[test]
283 fn test_link_nack() {
284 for command in [
285 CommandId::GenericNack,
286 CommandId::EnquireLink,
287 CommandId::EnquireLinkResp,
288 ] {
289 for state in [
290 SessionState::Open,
291 SessionState::Outbound,
292 SessionState::BoundTx,
293 SessionState::BoundRx,
294 SessionState::BoundTrx,
295 SessionState::Unbound,
296 ] {
297 assert!(state.can_send_as_esme(command));
298 assert!(state.can_send_as_mc(command));
299 assert!(state.can_receive_as_esme(command));
300 assert!(state.can_receive_as_mc(command));
301 }
302 }
303 }
304
305 #[test]
306 fn test_open_outbound() {
307 for state in [SessionState::Open, SessionState::Outbound] {
308 assert!(state.can_send_as_esme(CommandId::BindTransmitter));
309 assert!(state.can_send_as_esme(CommandId::BindTransceiver));
310 assert!(state.can_send_as_esme(CommandId::BindReceiver));
311 assert!(state.can_send_as_mc(CommandId::BindTransmitterResp));
312 assert!(state.can_send_as_mc(CommandId::BindTransceiverResp));
313 assert!(state.can_send_as_mc(CommandId::BindReceiverResp));
314 }
315 }
316
317 #[test]
318 fn test_tx() {
319 for command in ESME_BOUND_TX_COMMANDS {
320 assert!(SessionState::BoundTx.can_send_as_esme(command));
321 assert!(SessionState::BoundTx.can_receive_as_esme(command.matching_response()));
322 assert!(SessionState::BoundTx.can_receive_as_mc(command));
323 assert!(SessionState::BoundTx.can_send_as_mc(command.matching_response()));
324 }
325 }
326
327 #[test]
328 fn test_rx() {
329 for command in ESME_BOUND_RX_COMMANDS {
330 assert!(SessionState::BoundRx.can_send_as_esme(command));
331 assert!(SessionState::BoundRx.can_receive_as_esme(command.matching_request()));
332 assert!(SessionState::BoundRx.can_receive_as_mc(command));
333 assert!(SessionState::BoundRx.can_send_as_mc(command.matching_request()));
334 }
335 }
336
337 #[test]
338 fn test_trx() {
339 for command in ESME_BOUND_TX_COMMANDS {
340 assert!(SessionState::BoundTrx.can_send_as_esme(command));
341 assert!(SessionState::BoundTrx.can_receive_as_esme(command.matching_response()));
342 assert!(SessionState::BoundTrx.can_receive_as_mc(command));
343 assert!(SessionState::BoundTrx.can_send_as_mc(command.matching_response()));
344 }
345 for command in ESME_BOUND_RX_COMMANDS {
346 assert!(SessionState::BoundTrx.can_send_as_esme(command));
347 assert!(SessionState::BoundTrx.can_receive_as_esme(command.matching_request()));
348 assert!(SessionState::BoundTrx.can_receive_as_mc(command));
349 assert!(SessionState::BoundTrx.can_send_as_mc(command.matching_request()));
350 }
351 }
352}