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}