rusmpp/command/inner.rs
1use super::builder::CommandStatusBuilder;
2
3use crate::{CommandId, CommandStatus, Pdu};
4
5crate::create! {
6 /// `SMPP` command.
7 ///
8 /// The following PDU example illustrates how a `SMPP` PDU is decoded:
9 ///
10 /// Sample PDU (Values are shown in Hex format):
11 ///
12 /// 00 00 00 2F 00 00 00 02 00 00 00 00 00 00 00 01
13 ///
14 /// 53 4D 50 50 33 54 45 53 54 00 73 65 63 72 65 74
15 ///
16 /// 30 38 00 53 55 42 4D 49 54 31 00 50 01 01 00
17 ///
18 /// The 16-octet header would be decoded as follows:
19 ///
20 /// | Octets | Description |
21 /// | ------ | ----------- |
22 /// | 00 00 00 2F | Command Length (47) |
23 /// | 00 00 00 02 | Command ID (bind_transmitter) |
24 /// | 00 00 00 00 | Command Status (0) |
25 /// | 00 00 00 01 | Sequence Number (1)|
26 ///
27 /// The remaining data represents the PDU body (which in this example relates to the
28 /// bind_transmitter PDU). This is diagnosed as follows:
29 ///
30 /// | Octets | Value |
31 /// | ------ | ----- |
32 /// | 53 4D 50 50 33 54 45 53 54 00 | system_id (“SMPP3TEST”) |
33 /// | 73 65 63 72 65 74 30 38 00 | password (“secret08”) |
34 /// | 53 55 42 4D 49 54 31 00 | system_type (“SUBMIT1”) |
35 /// | 50 | interface_version (0x50 “V5.0 compliant”) |
36 /// | 01 | addr_ton (0x01) |
37 /// | 01 | addr_npi (0x01) |
38 /// | 00 | addr_range (NULL) |
39 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
40 #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
41 #[cfg_attr(feature = "serde", derive(::serde::Serialize))]
42 #[cfg_attr(feature = "serde-deserialize-unchecked", derive(::serde::Deserialize))]
43 pub struct Command {
44 /// See [`CommandId`]
45 id: CommandId,
46 /// See [`CommandStatus`]
47 pub status: CommandStatus,
48 /// The sequence_number represents a means of uniquely
49 /// identifying each PDU within a `SMPP` session. It also provides a means of correlating request
50 /// and response PDUs based on matching sequence number.
51 pub sequence_number: u32,
52 /// See [`Pdu`]
53 ///
54 /// Optional because incoming commands may not have a PDU.
55 @[key = id, length = unchecked]
56 pdu: Option<Pdu>,
57 }
58}
59
60impl Default for Command {
61 fn default() -> Self {
62 Self {
63 id: CommandId::EnquireLink,
64 status: CommandStatus::EsmeRok,
65 sequence_number: 0,
66 pdu: Some(Pdu::EnquireLink),
67 }
68 }
69}
70
71impl Command {
72 pub fn new(status: CommandStatus, sequence_number: u32, pdu: impl Into<Pdu>) -> Self {
73 let pdu = pdu.into();
74
75 let id = pdu.command_id();
76
77 Self {
78 id,
79 status,
80 sequence_number,
81 pdu: Some(pdu),
82 }
83 }
84
85 pub const fn new_const(status: CommandStatus, sequence_number: u32, pdu: Pdu) -> Self {
86 let id = pdu.command_id();
87
88 Self {
89 id,
90 status,
91 sequence_number,
92 pdu: Some(pdu),
93 }
94 }
95
96 #[inline]
97 pub const fn id(&self) -> CommandId {
98 self.id
99 }
100
101 #[inline]
102 pub const fn status(&self) -> CommandStatus {
103 self.status
104 }
105
106 #[inline]
107 pub const fn sequence_number(&self) -> u32 {
108 self.sequence_number
109 }
110
111 #[inline]
112 pub const fn pdu(&self) -> Option<&Pdu> {
113 self.pdu.as_ref()
114 }
115
116 #[inline]
117 pub fn set_pdu(&mut self, pdu: impl Into<Pdu>) {
118 let pdu = pdu.into();
119
120 self.id = pdu.command_id();
121
122 self.pdu = Some(pdu);
123 }
124
125 #[inline]
126 pub fn builder() -> CommandStatusBuilder {
127 Default::default()
128 }
129
130 /// Creates a new command from it's parts.
131 ///
132 /// # Note
133 ///
134 /// This may create invalid commands. It's up to the caller to ensure that the [`CommandId`] and [`Pdu`] match.
135 #[inline]
136 pub fn from_parts(parts: CommandParts) -> Self {
137 Self {
138 id: parts.id,
139 status: parts.status,
140 sequence_number: parts.sequence_number,
141 pdu: parts.pdu,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn encode_decode() {
152 crate::tests::encode_decode_with_length_test_instances::<Command>();
153 }
154}